VirtualBox

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

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

Main/Machine: get the conditions right when the group association can be changed, and fix a long standing issue with not being able to save settings of a VM which has saved state. While at it allow changing the VM description in this case, too.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 455.5 KB
Line 
1/* $Id: MachineImpl.cpp 42489 2012-07-31 19:02:49Z 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 mDragAndDropMode = DragAndDropMode_Disabled;
200 mGuestPropertyNotificationPatterns = "";
201
202 mFirmwareType = FirmwareType_BIOS;
203 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
204 mPointingHidType = PointingHidType_PS2Mouse;
205 mChipsetType = ChipsetType_PIIX3;
206 mEmulatedUSBCardReaderEnabled = FALSE;
207
208 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
209 mCPUAttached[i] = false;
210
211 mIoCacheEnabled = true;
212 mIoCacheSize = 5; /* 5MB */
213
214 /* Maximum CPU execution cap by default. */
215 mCpuExecutionCap = 100;
216}
217
218Machine::HWData::~HWData()
219{
220}
221
222/////////////////////////////////////////////////////////////////////////////
223// Machine::HDData structure
224/////////////////////////////////////////////////////////////////////////////
225
226Machine::MediaData::MediaData()
227{
228}
229
230Machine::MediaData::~MediaData()
231{
232}
233
234/////////////////////////////////////////////////////////////////////////////
235// Machine class
236/////////////////////////////////////////////////////////////////////////////
237
238// constructor / destructor
239/////////////////////////////////////////////////////////////////////////////
240
241Machine::Machine()
242 : mCollectorGuest(NULL),
243 mPeer(NULL),
244 mParent(NULL),
245 mSerialPorts(),
246 mParallelPorts(),
247 uRegistryNeedsSaving(0)
248{}
249
250Machine::~Machine()
251{}
252
253HRESULT Machine::FinalConstruct()
254{
255 LogFlowThisFunc(("\n"));
256 return BaseFinalConstruct();
257}
258
259void Machine::FinalRelease()
260{
261 LogFlowThisFunc(("\n"));
262 uninit();
263 BaseFinalRelease();
264}
265
266/**
267 * Initializes a new machine instance; this init() variant creates a new, empty machine.
268 * This gets called from VirtualBox::CreateMachine().
269 *
270 * @param aParent Associated parent object
271 * @param strConfigFile Local file system path to the VM settings file (can
272 * be relative to the VirtualBox config directory).
273 * @param strName name for the machine
274 * @param llGroups list of groups for the machine
275 * @param aOsType OS Type of this machine or NULL.
276 * @param aId UUID for the new machine.
277 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
278 *
279 * @return Success indicator. if not S_OK, the machine object is invalid
280 */
281HRESULT Machine::init(VirtualBox *aParent,
282 const Utf8Str &strConfigFile,
283 const Utf8Str &strName,
284 const StringsList &llGroups,
285 GuestOSType *aOsType,
286 const Guid &aId,
287 bool fForceOverwrite)
288{
289 LogFlowThisFuncEnter();
290 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
291
292 /* Enclose the state transition NotReady->InInit->Ready */
293 AutoInitSpan autoInitSpan(this);
294 AssertReturn(autoInitSpan.isOk(), E_FAIL);
295
296 HRESULT rc = initImpl(aParent, strConfigFile);
297 if (FAILED(rc)) return rc;
298
299 rc = tryCreateMachineConfigFile(fForceOverwrite);
300 if (FAILED(rc)) return rc;
301
302 if (SUCCEEDED(rc))
303 {
304 // create an empty machine config
305 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
306
307 rc = initDataAndChildObjects();
308 }
309
310 if (SUCCEEDED(rc))
311 {
312 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
313 mData->mAccessible = TRUE;
314
315 unconst(mData->mUuid) = aId;
316
317 mUserData->s.strName = strName;
318
319 mUserData->s.llGroups = llGroups;
320
321 // the "name sync" flag determines whether the machine directory gets renamed along
322 // with the machine file; say so if the settings file name is the same as the
323 // settings file parent directory (machine directory)
324 mUserData->s.fNameSync = isInOwnDir();
325
326 // initialize the default snapshots folder
327 rc = COMSETTER(SnapshotFolder)(NULL);
328 AssertComRC(rc);
329
330 if (aOsType)
331 {
332 /* Store OS type */
333 mUserData->s.strOsType = aOsType->id();
334
335 /* Apply BIOS defaults */
336 mBIOSSettings->applyDefaults(aOsType);
337
338 /* Apply network adapters defaults */
339 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
340 mNetworkAdapters[slot]->applyDefaults(aOsType);
341
342 /* Apply serial port defaults */
343 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
344 mSerialPorts[slot]->applyDefaults(aOsType);
345 }
346
347 /* At this point the changing of the current state modification
348 * flag is allowed. */
349 allowStateModification();
350
351 /* commit all changes made during the initialization */
352 commit();
353 }
354
355 /* Confirm a successful initialization when it's the case */
356 if (SUCCEEDED(rc))
357 {
358 if (mData->mAccessible)
359 autoInitSpan.setSucceeded();
360 else
361 autoInitSpan.setLimited();
362 }
363
364 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
365 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
366 mData->mRegistered,
367 mData->mAccessible,
368 rc));
369
370 LogFlowThisFuncLeave();
371
372 return rc;
373}
374
375/**
376 * Initializes a new instance with data from machine XML (formerly Init_Registered).
377 * Gets called in two modes:
378 *
379 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
380 * UUID is specified and we mark the machine as "registered";
381 *
382 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
383 * and the machine remains unregistered until RegisterMachine() is called.
384 *
385 * @param aParent Associated parent object
386 * @param aConfigFile Local file system path to the VM settings file (can
387 * be relative to the VirtualBox config directory).
388 * @param aId UUID of the machine or NULL (see above).
389 *
390 * @return Success indicator. if not S_OK, the machine object is invalid
391 */
392HRESULT Machine::init(VirtualBox *aParent,
393 const Utf8Str &strConfigFile,
394 const Guid *aId)
395{
396 LogFlowThisFuncEnter();
397 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
398
399 /* Enclose the state transition NotReady->InInit->Ready */
400 AutoInitSpan autoInitSpan(this);
401 AssertReturn(autoInitSpan.isOk(), E_FAIL);
402
403 HRESULT rc = initImpl(aParent, strConfigFile);
404 if (FAILED(rc)) return rc;
405
406 if (aId)
407 {
408 // loading a registered VM:
409 unconst(mData->mUuid) = *aId;
410 mData->mRegistered = TRUE;
411 // now load the settings from XML:
412 rc = registeredInit();
413 // this calls initDataAndChildObjects() and loadSettings()
414 }
415 else
416 {
417 // opening an unregistered VM (VirtualBox::OpenMachine()):
418 rc = initDataAndChildObjects();
419
420 if (SUCCEEDED(rc))
421 {
422 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
423 mData->mAccessible = TRUE;
424
425 try
426 {
427 // load and parse machine XML; this will throw on XML or logic errors
428 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
429
430 // reject VM UUID duplicates, they can happen if someone
431 // tries to register an already known VM config again
432 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
433 true /* fPermitInaccessible */,
434 false /* aDoSetError */,
435 NULL) != VBOX_E_OBJECT_NOT_FOUND)
436 {
437 throw setError(E_FAIL,
438 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
439 mData->m_strConfigFile.c_str());
440 }
441
442 // use UUID from machine config
443 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
444
445 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
446 NULL /* puuidRegistry */);
447 if (FAILED(rc)) throw rc;
448
449 /* At this point the changing of the current state modification
450 * flag is allowed. */
451 allowStateModification();
452
453 commit();
454 }
455 catch (HRESULT err)
456 {
457 /* we assume that error info is set by the thrower */
458 rc = err;
459 }
460 catch (...)
461 {
462 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
463 }
464 }
465 }
466
467 /* Confirm a successful initialization when it's the case */
468 if (SUCCEEDED(rc))
469 {
470 if (mData->mAccessible)
471 autoInitSpan.setSucceeded();
472 else
473 {
474 autoInitSpan.setLimited();
475
476 // uninit media from this machine's media registry, or else
477 // reloading the settings will fail
478 mParent->unregisterMachineMedia(getId());
479 }
480 }
481
482 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
483 "rc=%08X\n",
484 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
485 mData->mRegistered, mData->mAccessible, rc));
486
487 LogFlowThisFuncLeave();
488
489 return rc;
490}
491
492/**
493 * Initializes a new instance from a machine config that is already in memory
494 * (import OVF case). Since we are importing, the UUID in the machine
495 * config is ignored and we always generate a fresh one.
496 *
497 * @param strName Name for the new machine; this overrides what is specified in config and is used
498 * for the settings file as well.
499 * @param config Machine configuration loaded and parsed from XML.
500 *
501 * @return Success indicator. if not S_OK, the machine object is invalid
502 */
503HRESULT Machine::init(VirtualBox *aParent,
504 const Utf8Str &strName,
505 const settings::MachineConfigFile &config)
506{
507 LogFlowThisFuncEnter();
508
509 /* Enclose the state transition NotReady->InInit->Ready */
510 AutoInitSpan autoInitSpan(this);
511 AssertReturn(autoInitSpan.isOk(), E_FAIL);
512
513 Utf8Str strConfigFile;
514 aParent->getDefaultMachineFolder(strConfigFile);
515 strConfigFile.append(RTPATH_DELIMITER);
516 strConfigFile.append(strName);
517 strConfigFile.append(RTPATH_DELIMITER);
518 strConfigFile.append(strName);
519 strConfigFile.append(".vbox");
520
521 HRESULT rc = initImpl(aParent, strConfigFile);
522 if (FAILED(rc)) return rc;
523
524 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
525 if (FAILED(rc)) return rc;
526
527 rc = initDataAndChildObjects();
528
529 if (SUCCEEDED(rc))
530 {
531 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
532 mData->mAccessible = TRUE;
533
534 // create empty machine config for instance data
535 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
536
537 // generate fresh UUID, ignore machine config
538 unconst(mData->mUuid).create();
539
540 rc = loadMachineDataFromSettings(config,
541 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
542
543 // override VM name as well, it may be different
544 mUserData->s.strName = strName;
545
546 if (SUCCEEDED(rc))
547 {
548 /* At this point the changing of the current state modification
549 * flag is allowed. */
550 allowStateModification();
551
552 /* commit all changes made during the initialization */
553 commit();
554 }
555 }
556
557 /* Confirm a successful initialization when it's the case */
558 if (SUCCEEDED(rc))
559 {
560 if (mData->mAccessible)
561 autoInitSpan.setSucceeded();
562 else
563 {
564 autoInitSpan.setLimited();
565
566 // uninit media from this machine's media registry, or else
567 // reloading the settings will fail
568 mParent->unregisterMachineMedia(getId());
569 }
570 }
571
572 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
573 "rc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered, mData->mAccessible, rc));
576
577 LogFlowThisFuncLeave();
578
579 return rc;
580}
581
582/**
583 * Shared code between the various init() implementations.
584 * @param aParent
585 * @return
586 */
587HRESULT Machine::initImpl(VirtualBox *aParent,
588 const Utf8Str &strConfigFile)
589{
590 LogFlowThisFuncEnter();
591
592 AssertReturn(aParent, E_INVALIDARG);
593 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
594
595 HRESULT rc = S_OK;
596
597 /* share the parent weakly */
598 unconst(mParent) = aParent;
599
600 /* allocate the essential machine data structure (the rest will be
601 * allocated later by initDataAndChildObjects() */
602 mData.allocate();
603
604 /* memorize the config file name (as provided) */
605 mData->m_strConfigFile = strConfigFile;
606
607 /* get the full file name */
608 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
609 if (RT_FAILURE(vrc1))
610 return setError(VBOX_E_FILE_ERROR,
611 tr("Invalid machine settings file name '%s' (%Rrc)"),
612 strConfigFile.c_str(),
613 vrc1);
614
615 LogFlowThisFuncLeave();
616
617 return rc;
618}
619
620/**
621 * Tries to create a machine settings file in the path stored in the machine
622 * instance data. Used when a new machine is created to fail gracefully if
623 * the settings file could not be written (e.g. because machine dir is read-only).
624 * @return
625 */
626HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
627{
628 HRESULT rc = S_OK;
629
630 // when we create a new machine, we must be able to create the settings file
631 RTFILE f = NIL_RTFILE;
632 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
633 if ( RT_SUCCESS(vrc)
634 || vrc == VERR_SHARING_VIOLATION
635 )
636 {
637 if (RT_SUCCESS(vrc))
638 RTFileClose(f);
639 if (!fForceOverwrite)
640 rc = setError(VBOX_E_FILE_ERROR,
641 tr("Machine settings file '%s' already exists"),
642 mData->m_strConfigFileFull.c_str());
643 else
644 {
645 /* try to delete the config file, as otherwise the creation
646 * of a new settings file will fail. */
647 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
648 if (RT_FAILURE(vrc2))
649 rc = setError(VBOX_E_FILE_ERROR,
650 tr("Could not delete the existing settings file '%s' (%Rrc)"),
651 mData->m_strConfigFileFull.c_str(), vrc2);
652 }
653 }
654 else if ( vrc != VERR_FILE_NOT_FOUND
655 && vrc != VERR_PATH_NOT_FOUND
656 )
657 rc = setError(VBOX_E_FILE_ERROR,
658 tr("Invalid machine settings file name '%s' (%Rrc)"),
659 mData->m_strConfigFileFull.c_str(),
660 vrc);
661 return rc;
662}
663
664/**
665 * Initializes the registered machine by loading the settings file.
666 * This method is separated from #init() in order to make it possible to
667 * retry the operation after VirtualBox startup instead of refusing to
668 * startup the whole VirtualBox server in case if the settings file of some
669 * registered VM is invalid or inaccessible.
670 *
671 * @note Must be always called from this object's write lock
672 * (unless called from #init() that doesn't need any locking).
673 * @note Locks the mUSBController method for writing.
674 * @note Subclasses must not call this method.
675 */
676HRESULT Machine::registeredInit()
677{
678 AssertReturn(!isSessionMachine(), E_FAIL);
679 AssertReturn(!isSnapshotMachine(), E_FAIL);
680 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
681 AssertReturn(!mData->mAccessible, E_FAIL);
682
683 HRESULT rc = initDataAndChildObjects();
684
685 if (SUCCEEDED(rc))
686 {
687 /* Temporarily reset the registered flag in order to let setters
688 * potentially called from loadSettings() succeed (isMutable() used in
689 * all setters will return FALSE for a Machine instance if mRegistered
690 * is TRUE). */
691 mData->mRegistered = FALSE;
692
693 try
694 {
695 // load and parse machine XML; this will throw on XML or logic errors
696 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
697
698 if (mData->mUuid != mData->pMachineConfigFile->uuid)
699 throw setError(E_FAIL,
700 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
701 mData->pMachineConfigFile->uuid.raw(),
702 mData->m_strConfigFileFull.c_str(),
703 mData->mUuid.toString().c_str(),
704 mParent->settingsFilePath().c_str());
705
706 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
707 NULL /* const Guid *puuidRegistry */);
708 if (FAILED(rc)) throw rc;
709 }
710 catch (HRESULT err)
711 {
712 /* we assume that error info is set by the thrower */
713 rc = err;
714 }
715 catch (...)
716 {
717 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
718 }
719
720 /* Restore the registered flag (even on failure) */
721 mData->mRegistered = TRUE;
722 }
723
724 if (SUCCEEDED(rc))
725 {
726 /* Set mAccessible to TRUE only if we successfully locked and loaded
727 * the settings file */
728 mData->mAccessible = TRUE;
729
730 /* commit all changes made during loading the settings file */
731 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
732 /// @todo r=klaus for some reason the settings loading logic backs up
733 // the settings, and therefore a commit is needed. Should probably be changed.
734 }
735 else
736 {
737 /* If the machine is registered, then, instead of returning a
738 * failure, we mark it as inaccessible and set the result to
739 * success to give it a try later */
740
741 /* fetch the current error info */
742 mData->mAccessError = com::ErrorInfo();
743 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
744 mData->mUuid.raw(),
745 mData->mAccessError.getText().raw()));
746
747 /* rollback all changes */
748 rollback(false /* aNotify */);
749
750 // uninit media from this machine's media registry, or else
751 // reloading the settings will fail
752 mParent->unregisterMachineMedia(getId());
753
754 /* uninitialize the common part to make sure all data is reset to
755 * default (null) values */
756 uninitDataAndChildObjects();
757
758 rc = S_OK;
759 }
760
761 return rc;
762}
763
764/**
765 * Uninitializes the instance.
766 * Called either from FinalRelease() or by the parent when it gets destroyed.
767 *
768 * @note The caller of this method must make sure that this object
769 * a) doesn't have active callers on the current thread and b) is not locked
770 * by the current thread; otherwise uninit() will hang either a) due to
771 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
772 * a dead-lock caused by this thread waiting for all callers on the other
773 * threads are done but preventing them from doing so by holding a lock.
774 */
775void Machine::uninit()
776{
777 LogFlowThisFuncEnter();
778
779 Assert(!isWriteLockOnCurrentThread());
780
781 Assert(!uRegistryNeedsSaving);
782 if (uRegistryNeedsSaving)
783 {
784 AutoCaller autoCaller(this);
785 if (SUCCEEDED(autoCaller.rc()))
786 {
787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
788 saveSettings(NULL, Machine::SaveS_Force);
789 }
790 }
791
792 /* Enclose the state transition Ready->InUninit->NotReady */
793 AutoUninitSpan autoUninitSpan(this);
794 if (autoUninitSpan.uninitDone())
795 return;
796
797 Assert(!isSnapshotMachine());
798 Assert(!isSessionMachine());
799 Assert(!!mData);
800
801 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
802 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
803
804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
805
806 if (!mData->mSession.mMachine.isNull())
807 {
808 /* Theoretically, this can only happen if the VirtualBox server has been
809 * terminated while there were clients running that owned open direct
810 * sessions. Since in this case we are definitely called by
811 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
812 * won't happen on the client watcher thread (because it does
813 * VirtualBox::addCaller() for the duration of the
814 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
815 * cannot happen until the VirtualBox caller is released). This is
816 * important, because SessionMachine::uninit() cannot correctly operate
817 * after we return from this method (it expects the Machine instance is
818 * still valid). We'll call it ourselves below.
819 */
820 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
821 (SessionMachine*)mData->mSession.mMachine));
822
823 if (Global::IsOnlineOrTransient(mData->mMachineState))
824 {
825 LogWarningThisFunc(("Setting state to Aborted!\n"));
826 /* set machine state using SessionMachine reimplementation */
827 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
828 }
829
830 /*
831 * Uninitialize SessionMachine using public uninit() to indicate
832 * an unexpected uninitialization.
833 */
834 mData->mSession.mMachine->uninit();
835 /* SessionMachine::uninit() must set mSession.mMachine to null */
836 Assert(mData->mSession.mMachine.isNull());
837 }
838
839 // uninit media from this machine's media registry, if they're still there
840 Guid uuidMachine(getId());
841
842 /* XXX This will fail with
843 * "cannot be closed because it is still attached to 1 virtual machines"
844 * because at this point we did not call uninitDataAndChildObjects() yet
845 * and therefore also removeBackReference() for all these mediums was not called! */
846 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
847 mParent->unregisterMachineMedia(uuidMachine);
848
849 /* the lock is no more necessary (SessionMachine is uninitialized) */
850 alock.release();
851
852 // has machine been modified?
853 if (mData->flModifications)
854 {
855 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
856 rollback(false /* aNotify */);
857 }
858
859 if (mData->mAccessible)
860 uninitDataAndChildObjects();
861
862 /* free the essential data structure last */
863 mData.free();
864
865 LogFlowThisFuncLeave();
866}
867
868// IMachine properties
869/////////////////////////////////////////////////////////////////////////////
870
871STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
872{
873 CheckComArgOutPointerValid(aParent);
874
875 AutoLimitedCaller autoCaller(this);
876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
877
878 /* mParent is constant during life time, no need to lock */
879 ComObjPtr<VirtualBox> pVirtualBox(mParent);
880 pVirtualBox.queryInterfaceTo(aParent);
881
882 return S_OK;
883}
884
885STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
886{
887 CheckComArgOutPointerValid(aAccessible);
888
889 AutoLimitedCaller autoCaller(this);
890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
891
892 LogFlowThisFunc(("ENTER\n"));
893
894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
895
896 HRESULT rc = S_OK;
897
898 if (!mData->mAccessible)
899 {
900 /* try to initialize the VM once more if not accessible */
901
902 AutoReinitSpan autoReinitSpan(this);
903 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
904
905#ifdef DEBUG
906 LogFlowThisFunc(("Dumping media backreferences\n"));
907 mParent->dumpAllBackRefs();
908#endif
909
910 if (mData->pMachineConfigFile)
911 {
912 // reset the XML file to force loadSettings() (called from registeredInit())
913 // to parse it again; the file might have changed
914 delete mData->pMachineConfigFile;
915 mData->pMachineConfigFile = NULL;
916 }
917
918 rc = registeredInit();
919
920 if (SUCCEEDED(rc) && mData->mAccessible)
921 {
922 autoReinitSpan.setSucceeded();
923
924 /* make sure interesting parties will notice the accessibility
925 * state change */
926 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
927 mParent->onMachineDataChange(mData->mUuid);
928 }
929 }
930
931 if (SUCCEEDED(rc))
932 *aAccessible = mData->mAccessible;
933
934 LogFlowThisFuncLeave();
935
936 return rc;
937}
938
939STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
940{
941 CheckComArgOutPointerValid(aAccessError);
942
943 AutoLimitedCaller autoCaller(this);
944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
945
946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
947
948 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
949 {
950 /* return shortly */
951 aAccessError = NULL;
952 return S_OK;
953 }
954
955 HRESULT rc = S_OK;
956
957 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
958 rc = errorInfo.createObject();
959 if (SUCCEEDED(rc))
960 {
961 errorInfo->init(mData->mAccessError.getResultCode(),
962 mData->mAccessError.getInterfaceID().ref(),
963 Utf8Str(mData->mAccessError.getComponent()).c_str(),
964 Utf8Str(mData->mAccessError.getText()));
965 rc = errorInfo.queryInterfaceTo(aAccessError);
966 }
967
968 return rc;
969}
970
971STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
972{
973 CheckComArgOutPointerValid(aName);
974
975 AutoCaller autoCaller(this);
976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
977
978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
979
980 mUserData->s.strName.cloneTo(aName);
981
982 return S_OK;
983}
984
985STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
986{
987 CheckComArgStrNotEmptyOrNull(aName);
988
989 AutoCaller autoCaller(this);
990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
991
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995 if (test.isNotEmpty())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1011{
1012 CheckComArgOutPointerValid(aDescription);
1013
1014 AutoCaller autoCaller(this);
1015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1016
1017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1018
1019 mUserData->s.strDescription.cloneTo(aDescription);
1020
1021 return S_OK;
1022}
1023
1024STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1025{
1026 AutoCaller autoCaller(this);
1027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1028
1029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 // this can be done in principle in any state as it doesn't affect the VM
1032 // significantly, but play safe by not messing around while complex
1033 // activities are going on
1034 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1035 if (FAILED(rc)) return rc;
1036
1037 setModified(IsModified_MachineData);
1038 mUserData.backup();
1039 mUserData->s.strDescription = aDescription;
1040
1041 return S_OK;
1042}
1043
1044STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1045{
1046 CheckComArgOutPointerValid(aId);
1047
1048 AutoLimitedCaller autoCaller(this);
1049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1050
1051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 mData->mUuid.toUtf16().cloneTo(aId);
1054
1055 return S_OK;
1056}
1057
1058STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1059{
1060 CheckComArgOutSafeArrayPointerValid(aGroups);
1061
1062 AutoCaller autoCaller(this);
1063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1064
1065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1066 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1067 size_t i = 0;
1068 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1069 it != mUserData->s.llGroups.end();
1070 ++it, i++)
1071 {
1072 Bstr tmp = *it;
1073 tmp.cloneTo(&groups[i]);
1074 }
1075 groups.detachTo(ComSafeArrayOutArg(aGroups));
1076
1077 return S_OK;
1078}
1079
1080STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1081{
1082 AutoCaller autoCaller(this);
1083 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1084
1085 StringsList llGroups;
1086 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1087 if (FAILED(rc))
1088 return rc;
1089
1090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 // changing machine groups is possible while the VM is offline
1093 rc = checkStateDependency(OfflineStateDep);
1094 if (FAILED(rc)) return rc;
1095
1096 setModified(IsModified_MachineData);
1097 mUserData.backup();
1098 mUserData->s.llGroups = llGroups;
1099
1100 return S_OK;
1101}
1102
1103STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1104{
1105 CheckComArgOutPointerValid(aOSTypeId);
1106
1107 AutoCaller autoCaller(this);
1108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1109
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 mUserData->s.strOsType.cloneTo(aOSTypeId);
1113
1114 return S_OK;
1115}
1116
1117STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1118{
1119 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1120
1121 AutoCaller autoCaller(this);
1122 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1123
1124 /* look up the object by Id to check it is valid */
1125 ComPtr<IGuestOSType> guestOSType;
1126 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1127 if (FAILED(rc)) return rc;
1128
1129 /* when setting, always use the "etalon" value for consistency -- lookup
1130 * by ID is case-insensitive and the input value may have different case */
1131 Bstr osTypeId;
1132 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1133 if (FAILED(rc)) return rc;
1134
1135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 rc = checkStateDependency(MutableStateDep);
1138 if (FAILED(rc)) return rc;
1139
1140 setModified(IsModified_MachineData);
1141 mUserData.backup();
1142 mUserData->s.strOsType = osTypeId;
1143
1144 return S_OK;
1145}
1146
1147
1148STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1149{
1150 CheckComArgOutPointerValid(aFirmwareType);
1151
1152 AutoCaller autoCaller(this);
1153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1154
1155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1156
1157 *aFirmwareType = mHWData->mFirmwareType;
1158
1159 return S_OK;
1160}
1161
1162STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1163{
1164 AutoCaller autoCaller(this);
1165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 mHWData->mFirmwareType = aFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1179{
1180 CheckComArgOutPointerValid(aKeyboardHidType);
1181
1182 AutoCaller autoCaller(this);
1183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1184
1185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 *aKeyboardHidType = mHWData->mKeyboardHidType;
1188
1189 return S_OK;
1190}
1191
1192STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1193{
1194 AutoCaller autoCaller(this);
1195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1197
1198 HRESULT rc = checkStateDependency(MutableStateDep);
1199 if (FAILED(rc)) return rc;
1200
1201 setModified(IsModified_MachineData);
1202 mHWData.backup();
1203 mHWData->mKeyboardHidType = aKeyboardHidType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1209{
1210 CheckComArgOutPointerValid(aPointingHidType);
1211
1212 AutoCaller autoCaller(this);
1213 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1214
1215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1216
1217 *aPointingHidType = mHWData->mPointingHidType;
1218
1219 return S_OK;
1220}
1221
1222STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1223{
1224 AutoCaller autoCaller(this);
1225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1227
1228 HRESULT rc = checkStateDependency(MutableStateDep);
1229 if (FAILED(rc)) return rc;
1230
1231 setModified(IsModified_MachineData);
1232 mHWData.backup();
1233 mHWData->mPointingHidType = aPointingHidType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1239{
1240 CheckComArgOutPointerValid(aChipsetType);
1241
1242 AutoCaller autoCaller(this);
1243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1244
1245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1246
1247 *aChipsetType = mHWData->mChipsetType;
1248
1249 return S_OK;
1250}
1251
1252STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1253{
1254 AutoCaller autoCaller(this);
1255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aChipsetType != mHWData->mChipsetType)
1262 {
1263 setModified(IsModified_MachineData);
1264 mHWData.backup();
1265 mHWData->mChipsetType = aChipsetType;
1266
1267 // Resize network adapter array, to be finalized on commit/rollback.
1268 // We must not throw away entries yet, otherwise settings are lost
1269 // without a way to roll back.
1270 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1271 uint32_t oldCount = mNetworkAdapters.size();
1272 if (newCount > oldCount)
1273 {
1274 mNetworkAdapters.resize(newCount);
1275 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1276 {
1277 unconst(mNetworkAdapters[slot]).createObject();
1278 mNetworkAdapters[slot]->init(this, slot);
1279 }
1280 }
1281 }
1282
1283 return S_OK;
1284}
1285
1286STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1287{
1288 CheckComArgOutPointerValid(aHWVersion);
1289
1290 AutoCaller autoCaller(this);
1291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1292
1293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1294
1295 mHWData->mHWVersion.cloneTo(aHWVersion);
1296
1297 return S_OK;
1298}
1299
1300STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1301{
1302 /* check known version */
1303 Utf8Str hwVersion = aHWVersion;
1304 if ( hwVersion.compare("1") != 0
1305 && hwVersion.compare("2") != 0)
1306 return setError(E_INVALIDARG,
1307 tr("Invalid hardware version: %ls\n"), aHWVersion);
1308
1309 AutoCaller autoCaller(this);
1310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1311
1312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1313
1314 HRESULT rc = checkStateDependency(MutableStateDep);
1315 if (FAILED(rc)) return rc;
1316
1317 setModified(IsModified_MachineData);
1318 mHWData.backup();
1319 mHWData->mHWVersion = hwVersion;
1320
1321 return S_OK;
1322}
1323
1324STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1325{
1326 CheckComArgOutPointerValid(aUUID);
1327
1328 AutoCaller autoCaller(this);
1329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1330
1331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 if (!mHWData->mHardwareUUID.isEmpty())
1334 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1335 else
1336 mData->mUuid.toUtf16().cloneTo(aUUID);
1337
1338 return S_OK;
1339}
1340
1341STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1342{
1343 Guid hardwareUUID(aUUID);
1344 if (hardwareUUID.isEmpty())
1345 return E_INVALIDARG;
1346
1347 AutoCaller autoCaller(this);
1348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1349
1350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 HRESULT rc = checkStateDependency(MutableStateDep);
1353 if (FAILED(rc)) return rc;
1354
1355 setModified(IsModified_MachineData);
1356 mHWData.backup();
1357 if (hardwareUUID == mData->mUuid)
1358 mHWData->mHardwareUUID.clear();
1359 else
1360 mHWData->mHardwareUUID = hardwareUUID;
1361
1362 return S_OK;
1363}
1364
1365STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1366{
1367 CheckComArgOutPointerValid(memorySize);
1368
1369 AutoCaller autoCaller(this);
1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1371
1372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 *memorySize = mHWData->mMemorySize;
1375
1376 return S_OK;
1377}
1378
1379STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1380{
1381 /* check RAM limits */
1382 if ( memorySize < MM_RAM_MIN_IN_MB
1383 || memorySize > MM_RAM_MAX_IN_MB
1384 )
1385 return setError(E_INVALIDARG,
1386 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1387 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1388
1389 AutoCaller autoCaller(this);
1390 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1391
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 HRESULT rc = checkStateDependency(MutableStateDep);
1395 if (FAILED(rc)) return rc;
1396
1397 setModified(IsModified_MachineData);
1398 mHWData.backup();
1399 mHWData->mMemorySize = memorySize;
1400
1401 return S_OK;
1402}
1403
1404STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1405{
1406 CheckComArgOutPointerValid(CPUCount);
1407
1408 AutoCaller autoCaller(this);
1409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1410
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 *CPUCount = mHWData->mCPUCount;
1414
1415 return S_OK;
1416}
1417
1418STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1419{
1420 /* check CPU limits */
1421 if ( CPUCount < SchemaDefs::MinCPUCount
1422 || CPUCount > SchemaDefs::MaxCPUCount
1423 )
1424 return setError(E_INVALIDARG,
1425 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1426 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1427
1428 AutoCaller autoCaller(this);
1429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1434 if (mHWData->mCPUHotPlugEnabled)
1435 {
1436 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1437 {
1438 if (mHWData->mCPUAttached[idx])
1439 return setError(E_INVALIDARG,
1440 tr("There is still a CPU attached to socket %lu."
1441 "Detach the CPU before removing the socket"),
1442 CPUCount, idx+1);
1443 }
1444 }
1445
1446 HRESULT rc = checkStateDependency(MutableStateDep);
1447 if (FAILED(rc)) return rc;
1448
1449 setModified(IsModified_MachineData);
1450 mHWData.backup();
1451 mHWData->mCPUCount = CPUCount;
1452
1453 return S_OK;
1454}
1455
1456STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1457{
1458 CheckComArgOutPointerValid(aExecutionCap);
1459
1460 AutoCaller autoCaller(this);
1461 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1462
1463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1464
1465 *aExecutionCap = mHWData->mCpuExecutionCap;
1466
1467 return S_OK;
1468}
1469
1470STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1471{
1472 HRESULT rc = S_OK;
1473
1474 /* check throttle limits */
1475 if ( aExecutionCap < 1
1476 || aExecutionCap > 100
1477 )
1478 return setError(E_INVALIDARG,
1479 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1480 aExecutionCap, 1, 100);
1481
1482 AutoCaller autoCaller(this);
1483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1484
1485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 alock.release();
1488 rc = onCPUExecutionCapChange(aExecutionCap);
1489 alock.acquire();
1490 if (FAILED(rc)) return rc;
1491
1492 setModified(IsModified_MachineData);
1493 mHWData.backup();
1494 mHWData->mCpuExecutionCap = aExecutionCap;
1495
1496 /* Save settings if online - todo why is this required?? */
1497 if (Global::IsOnline(mData->mMachineState))
1498 saveSettings(NULL);
1499
1500 return S_OK;
1501}
1502
1503
1504STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1505{
1506 CheckComArgOutPointerValid(enabled);
1507
1508 AutoCaller autoCaller(this);
1509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1510
1511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1512
1513 *enabled = mHWData->mCPUHotPlugEnabled;
1514
1515 return S_OK;
1516}
1517
1518STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1519{
1520 HRESULT rc = S_OK;
1521
1522 AutoCaller autoCaller(this);
1523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1524
1525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1526
1527 rc = checkStateDependency(MutableStateDep);
1528 if (FAILED(rc)) return rc;
1529
1530 if (mHWData->mCPUHotPlugEnabled != enabled)
1531 {
1532 if (enabled)
1533 {
1534 setModified(IsModified_MachineData);
1535 mHWData.backup();
1536
1537 /* Add the amount of CPUs currently attached */
1538 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1539 {
1540 mHWData->mCPUAttached[i] = true;
1541 }
1542 }
1543 else
1544 {
1545 /*
1546 * We can disable hotplug only if the amount of maximum CPUs is equal
1547 * to the amount of attached CPUs
1548 */
1549 unsigned cCpusAttached = 0;
1550 unsigned iHighestId = 0;
1551
1552 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1553 {
1554 if (mHWData->mCPUAttached[i])
1555 {
1556 cCpusAttached++;
1557 iHighestId = i;
1558 }
1559 }
1560
1561 if ( (cCpusAttached != mHWData->mCPUCount)
1562 || (iHighestId >= mHWData->mCPUCount))
1563 return setError(E_INVALIDARG,
1564 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1565
1566 setModified(IsModified_MachineData);
1567 mHWData.backup();
1568 }
1569 }
1570
1571 mHWData->mCPUHotPlugEnabled = enabled;
1572
1573 return rc;
1574}
1575
1576STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1577{
1578#ifdef VBOX_WITH_USB_CARDREADER
1579 CheckComArgOutPointerValid(enabled);
1580
1581 AutoCaller autoCaller(this);
1582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1583
1584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1585
1586 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1587
1588 return S_OK;
1589#else
1590 NOREF(enabled);
1591 return E_NOTIMPL;
1592#endif
1593}
1594
1595STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1596{
1597#ifdef VBOX_WITH_USB_CARDREADER
1598 AutoCaller autoCaller(this);
1599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 HRESULT rc = checkStateDependency(MutableStateDep);
1603 if (FAILED(rc)) return rc;
1604
1605 setModified(IsModified_MachineData);
1606 mHWData.backup();
1607 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1608
1609 return S_OK;
1610#else
1611 NOREF(enabled);
1612 return E_NOTIMPL;
1613#endif
1614}
1615
1616STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1617{
1618 NOREF(enabled);
1619 return E_NOTIMPL;
1620}
1621
1622STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1623{
1624 NOREF(enabled);
1625 return E_NOTIMPL;
1626}
1627
1628STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1629{
1630 CheckComArgOutPointerValid(enabled);
1631
1632 AutoCaller autoCaller(this);
1633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 *enabled = mHWData->mHpetEnabled;
1637
1638 return S_OK;
1639}
1640
1641STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1642{
1643 HRESULT rc = S_OK;
1644
1645 AutoCaller autoCaller(this);
1646 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1648
1649 rc = checkStateDependency(MutableStateDep);
1650 if (FAILED(rc)) return rc;
1651
1652 setModified(IsModified_MachineData);
1653 mHWData.backup();
1654
1655 mHWData->mHpetEnabled = enabled;
1656
1657 return rc;
1658}
1659
1660STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1661{
1662 CheckComArgOutPointerValid(memorySize);
1663
1664 AutoCaller autoCaller(this);
1665 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1666
1667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1668
1669 *memorySize = mHWData->mVRAMSize;
1670
1671 return S_OK;
1672}
1673
1674STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1675{
1676 /* check VRAM limits */
1677 if (memorySize < SchemaDefs::MinGuestVRAM ||
1678 memorySize > SchemaDefs::MaxGuestVRAM)
1679 return setError(E_INVALIDARG,
1680 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1681 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685
1686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 HRESULT rc = checkStateDependency(MutableStateDep);
1689 if (FAILED(rc)) return rc;
1690
1691 setModified(IsModified_MachineData);
1692 mHWData.backup();
1693 mHWData->mVRAMSize = memorySize;
1694
1695 return S_OK;
1696}
1697
1698/** @todo this method should not be public */
1699STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1700{
1701 CheckComArgOutPointerValid(memoryBalloonSize);
1702
1703 AutoCaller autoCaller(this);
1704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1705
1706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1707
1708 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1709
1710 return S_OK;
1711}
1712
1713/**
1714 * Set the memory balloon size.
1715 *
1716 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1717 * we have to make sure that we never call IGuest from here.
1718 */
1719STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1720{
1721 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1722#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1723 /* check limits */
1724 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1725 return setError(E_INVALIDARG,
1726 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1727 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1728
1729 AutoCaller autoCaller(this);
1730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1731
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1737
1738 return S_OK;
1739#else
1740 NOREF(memoryBalloonSize);
1741 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1742#endif
1743}
1744
1745STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1746{
1747 CheckComArgOutPointerValid(enabled);
1748
1749 AutoCaller autoCaller(this);
1750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1751
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 *enabled = mHWData->mPageFusionEnabled;
1755 return S_OK;
1756}
1757
1758STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1759{
1760#ifdef VBOX_WITH_PAGE_SHARING
1761 AutoCaller autoCaller(this);
1762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1767 setModified(IsModified_MachineData);
1768 mHWData.backup();
1769 mHWData->mPageFusionEnabled = enabled;
1770 return S_OK;
1771#else
1772 NOREF(enabled);
1773 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1774#endif
1775}
1776
1777STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1778{
1779 CheckComArgOutPointerValid(enabled);
1780
1781 AutoCaller autoCaller(this);
1782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1783
1784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1785
1786 *enabled = mHWData->mAccelerate3DEnabled;
1787
1788 return S_OK;
1789}
1790
1791STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1792{
1793 AutoCaller autoCaller(this);
1794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1795
1796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1797
1798 HRESULT rc = checkStateDependency(MutableStateDep);
1799 if (FAILED(rc)) return rc;
1800
1801 /** @todo check validity! */
1802
1803 setModified(IsModified_MachineData);
1804 mHWData.backup();
1805 mHWData->mAccelerate3DEnabled = enable;
1806
1807 return S_OK;
1808}
1809
1810
1811STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1812{
1813 CheckComArgOutPointerValid(enabled);
1814
1815 AutoCaller autoCaller(this);
1816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1817
1818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 *enabled = mHWData->mAccelerate2DVideoEnabled;
1821
1822 return S_OK;
1823}
1824
1825STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1826{
1827 AutoCaller autoCaller(this);
1828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1829
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 HRESULT rc = checkStateDependency(MutableStateDep);
1833 if (FAILED(rc)) return rc;
1834
1835 /** @todo check validity! */
1836
1837 setModified(IsModified_MachineData);
1838 mHWData.backup();
1839 mHWData->mAccelerate2DVideoEnabled = enable;
1840
1841 return S_OK;
1842}
1843
1844STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1845{
1846 CheckComArgOutPointerValid(monitorCount);
1847
1848 AutoCaller autoCaller(this);
1849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1850
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 *monitorCount = mHWData->mMonitorCount;
1854
1855 return S_OK;
1856}
1857
1858STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1859{
1860 /* make sure monitor count is a sensible number */
1861 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1862 return setError(E_INVALIDARG,
1863 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1864 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1865
1866 AutoCaller autoCaller(this);
1867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1868
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 HRESULT rc = checkStateDependency(MutableStateDep);
1872 if (FAILED(rc)) return rc;
1873
1874 setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mMonitorCount = monitorCount;
1877
1878 return S_OK;
1879}
1880
1881STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1882{
1883 CheckComArgOutPointerValid(biosSettings);
1884
1885 AutoCaller autoCaller(this);
1886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1887
1888 /* mBIOSSettings is constant during life time, no need to lock */
1889 mBIOSSettings.queryInterfaceTo(biosSettings);
1890
1891 return S_OK;
1892}
1893
1894STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1895{
1896 CheckComArgOutPointerValid(aVal);
1897
1898 AutoCaller autoCaller(this);
1899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1900
1901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1902
1903 switch(property)
1904 {
1905 case CPUPropertyType_PAE:
1906 *aVal = mHWData->mPAEEnabled;
1907 break;
1908
1909 case CPUPropertyType_Synthetic:
1910 *aVal = mHWData->mSyntheticCpu;
1911 break;
1912
1913 default:
1914 return E_INVALIDARG;
1915 }
1916 return S_OK;
1917}
1918
1919STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1920{
1921 AutoCaller autoCaller(this);
1922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1923
1924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 HRESULT rc = checkStateDependency(MutableStateDep);
1927 if (FAILED(rc)) return rc;
1928
1929 switch(property)
1930 {
1931 case CPUPropertyType_PAE:
1932 setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mPAEEnabled = !!aVal;
1935 break;
1936
1937 case CPUPropertyType_Synthetic:
1938 setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mSyntheticCpu = !!aVal;
1941 break;
1942
1943 default:
1944 return E_INVALIDARG;
1945 }
1946 return S_OK;
1947}
1948
1949STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1950{
1951 CheckComArgOutPointerValid(aValEax);
1952 CheckComArgOutPointerValid(aValEbx);
1953 CheckComArgOutPointerValid(aValEcx);
1954 CheckComArgOutPointerValid(aValEdx);
1955
1956 AutoCaller autoCaller(this);
1957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1958
1959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1960
1961 switch(aId)
1962 {
1963 case 0x0:
1964 case 0x1:
1965 case 0x2:
1966 case 0x3:
1967 case 0x4:
1968 case 0x5:
1969 case 0x6:
1970 case 0x7:
1971 case 0x8:
1972 case 0x9:
1973 case 0xA:
1974 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1975 return E_INVALIDARG;
1976
1977 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1978 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1979 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1980 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1981 break;
1982
1983 case 0x80000000:
1984 case 0x80000001:
1985 case 0x80000002:
1986 case 0x80000003:
1987 case 0x80000004:
1988 case 0x80000005:
1989 case 0x80000006:
1990 case 0x80000007:
1991 case 0x80000008:
1992 case 0x80000009:
1993 case 0x8000000A:
1994 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1995 return E_INVALIDARG;
1996
1997 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1998 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1999 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2000 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2001 break;
2002
2003 default:
2004 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2005 }
2006 return S_OK;
2007}
2008
2009STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2010{
2011 AutoCaller autoCaller(this);
2012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2013
2014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2015
2016 HRESULT rc = checkStateDependency(MutableStateDep);
2017 if (FAILED(rc)) return rc;
2018
2019 switch(aId)
2020 {
2021 case 0x0:
2022 case 0x1:
2023 case 0x2:
2024 case 0x3:
2025 case 0x4:
2026 case 0x5:
2027 case 0x6:
2028 case 0x7:
2029 case 0x8:
2030 case 0x9:
2031 case 0xA:
2032 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2033 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2034 setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2037 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2038 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2039 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2040 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2041 break;
2042
2043 case 0x80000000:
2044 case 0x80000001:
2045 case 0x80000002:
2046 case 0x80000003:
2047 case 0x80000004:
2048 case 0x80000005:
2049 case 0x80000006:
2050 case 0x80000007:
2051 case 0x80000008:
2052 case 0x80000009:
2053 case 0x8000000A:
2054 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2055 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2056 setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2059 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2060 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2061 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2062 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2063 break;
2064
2065 default:
2066 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2067 }
2068 return S_OK;
2069}
2070
2071STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2072{
2073 AutoCaller autoCaller(this);
2074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2075
2076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 HRESULT rc = checkStateDependency(MutableStateDep);
2079 if (FAILED(rc)) return rc;
2080
2081 switch(aId)
2082 {
2083 case 0x0:
2084 case 0x1:
2085 case 0x2:
2086 case 0x3:
2087 case 0x4:
2088 case 0x5:
2089 case 0x6:
2090 case 0x7:
2091 case 0x8:
2092 case 0x9:
2093 case 0xA:
2094 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2095 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2096 setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 /* Invalidate leaf. */
2099 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2100 break;
2101
2102 case 0x80000000:
2103 case 0x80000001:
2104 case 0x80000002:
2105 case 0x80000003:
2106 case 0x80000004:
2107 case 0x80000005:
2108 case 0x80000006:
2109 case 0x80000007:
2110 case 0x80000008:
2111 case 0x80000009:
2112 case 0x8000000A:
2113 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2114 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2115 setModified(IsModified_MachineData);
2116 mHWData.backup();
2117 /* Invalidate leaf. */
2118 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2119 break;
2120
2121 default:
2122 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2123 }
2124 return S_OK;
2125}
2126
2127STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2128{
2129 AutoCaller autoCaller(this);
2130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2131
2132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 HRESULT rc = checkStateDependency(MutableStateDep);
2135 if (FAILED(rc)) return rc;
2136
2137 setModified(IsModified_MachineData);
2138 mHWData.backup();
2139
2140 /* Invalidate all standard leafs. */
2141 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2142 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2143
2144 /* Invalidate all extended leafs. */
2145 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2146 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2147
2148 return S_OK;
2149}
2150
2151STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2152{
2153 CheckComArgOutPointerValid(aVal);
2154
2155 AutoCaller autoCaller(this);
2156 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2157
2158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2159
2160 switch(property)
2161 {
2162 case HWVirtExPropertyType_Enabled:
2163 *aVal = mHWData->mHWVirtExEnabled;
2164 break;
2165
2166 case HWVirtExPropertyType_Exclusive:
2167 *aVal = mHWData->mHWVirtExExclusive;
2168 break;
2169
2170 case HWVirtExPropertyType_VPID:
2171 *aVal = mHWData->mHWVirtExVPIDEnabled;
2172 break;
2173
2174 case HWVirtExPropertyType_NestedPaging:
2175 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2176 break;
2177
2178 case HWVirtExPropertyType_LargePages:
2179 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2180#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2181 *aVal = FALSE;
2182#endif
2183 break;
2184
2185 case HWVirtExPropertyType_Force:
2186 *aVal = mHWData->mHWVirtExForceEnabled;
2187 break;
2188
2189 default:
2190 return E_INVALIDARG;
2191 }
2192 return S_OK;
2193}
2194
2195STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2196{
2197 AutoCaller autoCaller(this);
2198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2199
2200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 HRESULT rc = checkStateDependency(MutableStateDep);
2203 if (FAILED(rc)) return rc;
2204
2205 switch(property)
2206 {
2207 case HWVirtExPropertyType_Enabled:
2208 setModified(IsModified_MachineData);
2209 mHWData.backup();
2210 mHWData->mHWVirtExEnabled = !!aVal;
2211 break;
2212
2213 case HWVirtExPropertyType_Exclusive:
2214 setModified(IsModified_MachineData);
2215 mHWData.backup();
2216 mHWData->mHWVirtExExclusive = !!aVal;
2217 break;
2218
2219 case HWVirtExPropertyType_VPID:
2220 setModified(IsModified_MachineData);
2221 mHWData.backup();
2222 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2223 break;
2224
2225 case HWVirtExPropertyType_NestedPaging:
2226 setModified(IsModified_MachineData);
2227 mHWData.backup();
2228 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2229 break;
2230
2231 case HWVirtExPropertyType_LargePages:
2232 setModified(IsModified_MachineData);
2233 mHWData.backup();
2234 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2235 break;
2236
2237 case HWVirtExPropertyType_Force:
2238 setModified(IsModified_MachineData);
2239 mHWData.backup();
2240 mHWData->mHWVirtExForceEnabled = !!aVal;
2241 break;
2242
2243 default:
2244 return E_INVALIDARG;
2245 }
2246
2247 return S_OK;
2248}
2249
2250STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2251{
2252 CheckComArgOutPointerValid(aSnapshotFolder);
2253
2254 AutoCaller autoCaller(this);
2255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2256
2257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 Utf8Str strFullSnapshotFolder;
2260 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2261 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2262
2263 return S_OK;
2264}
2265
2266STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2267{
2268 /* @todo (r=dmik):
2269 * 1. Allow to change the name of the snapshot folder containing snapshots
2270 * 2. Rename the folder on disk instead of just changing the property
2271 * value (to be smart and not to leave garbage). Note that it cannot be
2272 * done here because the change may be rolled back. Thus, the right
2273 * place is #saveSettings().
2274 */
2275
2276 AutoCaller autoCaller(this);
2277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2278
2279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2280
2281 HRESULT rc = checkStateDependency(MutableStateDep);
2282 if (FAILED(rc)) return rc;
2283
2284 if (!mData->mCurrentSnapshot.isNull())
2285 return setError(E_FAIL,
2286 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2287
2288 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2289
2290 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2291 if (strSnapshotFolder.isEmpty())
2292 strSnapshotFolder = "Snapshots";
2293 int vrc = calculateFullPath(strSnapshotFolder,
2294 strSnapshotFolder);
2295 if (RT_FAILURE(vrc))
2296 return setError(E_FAIL,
2297 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2298 aSnapshotFolder, vrc);
2299
2300 setModified(IsModified_MachineData);
2301 mUserData.backup();
2302
2303 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2304
2305 return S_OK;
2306}
2307
2308STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2309{
2310 CheckComArgOutSafeArrayPointerValid(aAttachments);
2311
2312 AutoCaller autoCaller(this);
2313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2314
2315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2316
2317 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2318 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2319
2320 return S_OK;
2321}
2322
2323STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2324{
2325 CheckComArgOutPointerValid(vrdeServer);
2326
2327 AutoCaller autoCaller(this);
2328 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2329
2330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2331
2332 Assert(!!mVRDEServer);
2333 mVRDEServer.queryInterfaceTo(vrdeServer);
2334
2335 return S_OK;
2336}
2337
2338STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2339{
2340 CheckComArgOutPointerValid(audioAdapter);
2341
2342 AutoCaller autoCaller(this);
2343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2344
2345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2346
2347 mAudioAdapter.queryInterfaceTo(audioAdapter);
2348 return S_OK;
2349}
2350
2351STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2352{
2353#ifdef VBOX_WITH_VUSB
2354 CheckComArgOutPointerValid(aUSBController);
2355
2356 AutoCaller autoCaller(this);
2357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2358
2359 clearError();
2360 MultiResult rc(S_OK);
2361
2362# ifdef VBOX_WITH_USB
2363 rc = mParent->host()->checkUSBProxyService();
2364 if (FAILED(rc)) return rc;
2365# endif
2366
2367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 return rc = mUSBController.queryInterfaceTo(aUSBController);
2370#else
2371 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2372 * extended error info to indicate that USB is simply not available
2373 * (w/o treating it as a failure), for example, as in OSE */
2374 NOREF(aUSBController);
2375 ReturnComNotImplemented();
2376#endif /* VBOX_WITH_VUSB */
2377}
2378
2379STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2380{
2381 CheckComArgOutPointerValid(aFilePath);
2382
2383 AutoLimitedCaller autoCaller(this);
2384 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2385
2386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 mData->m_strConfigFileFull.cloneTo(aFilePath);
2389 return S_OK;
2390}
2391
2392STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2393{
2394 CheckComArgOutPointerValid(aModified);
2395
2396 AutoCaller autoCaller(this);
2397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2398
2399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2400
2401 HRESULT rc = checkStateDependency(MutableStateDep);
2402 if (FAILED(rc)) return rc;
2403
2404 if (!mData->pMachineConfigFile->fileExists())
2405 // this is a new machine, and no config file exists yet:
2406 *aModified = TRUE;
2407 else
2408 *aModified = (mData->flModifications != 0);
2409
2410 return S_OK;
2411}
2412
2413STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2414{
2415 CheckComArgOutPointerValid(aSessionState);
2416
2417 AutoCaller autoCaller(this);
2418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2419
2420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 *aSessionState = mData->mSession.mState;
2423
2424 return S_OK;
2425}
2426
2427STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2428{
2429 CheckComArgOutPointerValid(aSessionType);
2430
2431 AutoCaller autoCaller(this);
2432 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2433
2434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2435
2436 mData->mSession.mType.cloneTo(aSessionType);
2437
2438 return S_OK;
2439}
2440
2441STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2442{
2443 CheckComArgOutPointerValid(aSessionPid);
2444
2445 AutoCaller autoCaller(this);
2446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2447
2448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2449
2450 *aSessionPid = mData->mSession.mPid;
2451
2452 return S_OK;
2453}
2454
2455STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2456{
2457 CheckComArgOutPointerValid(machineState);
2458
2459 AutoCaller autoCaller(this);
2460 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2461
2462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2463
2464 *machineState = mData->mMachineState;
2465
2466 return S_OK;
2467}
2468
2469STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2470{
2471 CheckComArgOutPointerValid(aLastStateChange);
2472
2473 AutoCaller autoCaller(this);
2474 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2475
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2479
2480 return S_OK;
2481}
2482
2483STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2484{
2485 CheckComArgOutPointerValid(aStateFilePath);
2486
2487 AutoCaller autoCaller(this);
2488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2489
2490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2493
2494 return S_OK;
2495}
2496
2497STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2498{
2499 CheckComArgOutPointerValid(aLogFolder);
2500
2501 AutoCaller autoCaller(this);
2502 AssertComRCReturnRC(autoCaller.rc());
2503
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 Utf8Str logFolder;
2507 getLogFolder(logFolder);
2508 logFolder.cloneTo(aLogFolder);
2509
2510 return S_OK;
2511}
2512
2513STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2514{
2515 CheckComArgOutPointerValid(aCurrentSnapshot);
2516
2517 AutoCaller autoCaller(this);
2518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2519
2520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2521
2522 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2523
2524 return S_OK;
2525}
2526
2527STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2528{
2529 CheckComArgOutPointerValid(aSnapshotCount);
2530
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2537 ? 0
2538 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2539
2540 return S_OK;
2541}
2542
2543STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2544{
2545 CheckComArgOutPointerValid(aCurrentStateModified);
2546
2547 AutoCaller autoCaller(this);
2548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2549
2550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2551
2552 /* Note: for machines with no snapshots, we always return FALSE
2553 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2554 * reasons :) */
2555
2556 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2557 ? FALSE
2558 : mData->mCurrentStateModified;
2559
2560 return S_OK;
2561}
2562
2563STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2564{
2565 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2566
2567 AutoCaller autoCaller(this);
2568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2569
2570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2571
2572 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2573 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2574
2575 return S_OK;
2576}
2577
2578STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2579{
2580 CheckComArgOutPointerValid(aClipboardMode);
2581
2582 AutoCaller autoCaller(this);
2583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2584
2585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2586
2587 *aClipboardMode = mHWData->mClipboardMode;
2588
2589 return S_OK;
2590}
2591
2592STDMETHODIMP
2593Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2594{
2595 HRESULT rc = S_OK;
2596
2597 AutoCaller autoCaller(this);
2598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2599
2600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2601
2602 alock.release();
2603 rc = onClipboardModeChange(aClipboardMode);
2604 alock.acquire();
2605 if (FAILED(rc)) return rc;
2606
2607 setModified(IsModified_MachineData);
2608 mHWData.backup();
2609 mHWData->mClipboardMode = aClipboardMode;
2610
2611 /* Save settings if online - todo why is this required?? */
2612 if (Global::IsOnline(mData->mMachineState))
2613 saveSettings(NULL);
2614
2615 return S_OK;
2616}
2617
2618STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2619{
2620 CheckComArgOutPointerValid(aDragAndDropMode);
2621
2622 AutoCaller autoCaller(this);
2623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 *aDragAndDropMode = mHWData->mDragAndDropMode;
2628
2629 return S_OK;
2630}
2631
2632STDMETHODIMP
2633Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2634{
2635 HRESULT rc = S_OK;
2636
2637 AutoCaller autoCaller(this);
2638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2639
2640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 alock.release();
2643 rc = onDragAndDropModeChange(aDragAndDropMode);
2644 alock.acquire();
2645 if (FAILED(rc)) return rc;
2646
2647 setModified(IsModified_MachineData);
2648 mHWData.backup();
2649 mHWData->mDragAndDropMode = aDragAndDropMode;
2650
2651 /* Save settings if online - todo why is this required?? */
2652 if (Global::IsOnline(mData->mMachineState))
2653 saveSettings(NULL);
2654
2655 return S_OK;
2656}
2657
2658STDMETHODIMP
2659Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2660{
2661 CheckComArgOutPointerValid(aPatterns);
2662
2663 AutoCaller autoCaller(this);
2664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 try
2669 {
2670 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2671 }
2672 catch (...)
2673 {
2674 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2675 }
2676
2677 return S_OK;
2678}
2679
2680STDMETHODIMP
2681Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2682{
2683 AutoCaller autoCaller(this);
2684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2685
2686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 HRESULT rc = checkStateDependency(MutableStateDep);
2689 if (FAILED(rc)) return rc;
2690
2691 setModified(IsModified_MachineData);
2692 mHWData.backup();
2693 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2694 return rc;
2695}
2696
2697STDMETHODIMP
2698Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2699{
2700 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2701
2702 AutoCaller autoCaller(this);
2703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2704
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2708 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2709
2710 return S_OK;
2711}
2712
2713STDMETHODIMP
2714Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2715{
2716 CheckComArgOutPointerValid(aEnabled);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aEnabled = mUserData->s.fTeleporterEnabled;
2724
2725 return S_OK;
2726}
2727
2728STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2729{
2730 AutoCaller autoCaller(this);
2731 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2732
2733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2734
2735 /* Only allow it to be set to true when PoweredOff or Aborted.
2736 (Clearing it is always permitted.) */
2737 if ( aEnabled
2738 && mData->mRegistered
2739 && ( !isSessionMachine()
2740 || ( mData->mMachineState != MachineState_PoweredOff
2741 && mData->mMachineState != MachineState_Teleported
2742 && mData->mMachineState != MachineState_Aborted
2743 )
2744 )
2745 )
2746 return setError(VBOX_E_INVALID_VM_STATE,
2747 tr("The machine is not powered off (state is %s)"),
2748 Global::stringifyMachineState(mData->mMachineState));
2749
2750 setModified(IsModified_MachineData);
2751 mUserData.backup();
2752 mUserData->s.fTeleporterEnabled = !!aEnabled;
2753
2754 return S_OK;
2755}
2756
2757STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2758{
2759 CheckComArgOutPointerValid(aPort);
2760
2761 AutoCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2767
2768 return S_OK;
2769}
2770
2771STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2772{
2773 if (aPort >= _64K)
2774 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2775
2776 AutoCaller autoCaller(this);
2777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2778
2779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 HRESULT rc = checkStateDependency(MutableStateDep);
2782 if (FAILED(rc)) return rc;
2783
2784 setModified(IsModified_MachineData);
2785 mUserData.backup();
2786 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2792{
2793 CheckComArgOutPointerValid(aAddress);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2806{
2807 AutoCaller autoCaller(this);
2808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2809
2810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 HRESULT rc = checkStateDependency(MutableStateDep);
2813 if (FAILED(rc)) return rc;
2814
2815 setModified(IsModified_MachineData);
2816 mUserData.backup();
2817 mUserData->s.strTeleporterAddress = aAddress;
2818
2819 return S_OK;
2820}
2821
2822STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2823{
2824 CheckComArgOutPointerValid(aPassword);
2825
2826 AutoCaller autoCaller(this);
2827 HRESULT hrc = autoCaller.rc();
2828 if (SUCCEEDED(hrc))
2829 {
2830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2831 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2832 }
2833
2834 return hrc;
2835}
2836
2837STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2838{
2839 /*
2840 * Hash the password first.
2841 */
2842 Utf8Str strPassword(aPassword);
2843 if (!strPassword.isEmpty())
2844 {
2845 if (VBoxIsPasswordHashed(&strPassword))
2846 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2847 VBoxHashPassword(&strPassword);
2848 }
2849
2850 /*
2851 * Do the update.
2852 */
2853 AutoCaller autoCaller(this);
2854 HRESULT hrc = autoCaller.rc();
2855 if (SUCCEEDED(hrc))
2856 {
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858 hrc = checkStateDependency(MutableStateDep);
2859 if (SUCCEEDED(hrc))
2860 {
2861 setModified(IsModified_MachineData);
2862 mUserData.backup();
2863 mUserData->s.strTeleporterPassword = strPassword;
2864 }
2865 }
2866
2867 return hrc;
2868}
2869
2870STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2871{
2872 CheckComArgOutPointerValid(aState);
2873
2874 AutoCaller autoCaller(this);
2875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2876
2877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 *aState = mUserData->s.enmFaultToleranceState;
2880 return S_OK;
2881}
2882
2883STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2884{
2885 AutoCaller autoCaller(this);
2886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2887
2888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2889
2890 /* @todo deal with running state change. */
2891 HRESULT rc = checkStateDependency(MutableStateDep);
2892 if (FAILED(rc)) return rc;
2893
2894 setModified(IsModified_MachineData);
2895 mUserData.backup();
2896 mUserData->s.enmFaultToleranceState = aState;
2897 return S_OK;
2898}
2899
2900STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2901{
2902 CheckComArgOutPointerValid(aAddress);
2903
2904 AutoCaller autoCaller(this);
2905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2906
2907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2910 return S_OK;
2911}
2912
2913STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2914{
2915 AutoCaller autoCaller(this);
2916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2917
2918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2919
2920 /* @todo deal with running state change. */
2921 HRESULT rc = checkStateDependency(MutableStateDep);
2922 if (FAILED(rc)) return rc;
2923
2924 setModified(IsModified_MachineData);
2925 mUserData.backup();
2926 mUserData->s.strFaultToleranceAddress = aAddress;
2927 return S_OK;
2928}
2929
2930STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2931{
2932 CheckComArgOutPointerValid(aPort);
2933
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 *aPort = mUserData->s.uFaultTolerancePort;
2940 return S_OK;
2941}
2942
2943STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2944{
2945 AutoCaller autoCaller(this);
2946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2947
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 /* @todo deal with running state change. */
2951 HRESULT rc = checkStateDependency(MutableStateDep);
2952 if (FAILED(rc)) return rc;
2953
2954 setModified(IsModified_MachineData);
2955 mUserData.backup();
2956 mUserData->s.uFaultTolerancePort = aPort;
2957 return S_OK;
2958}
2959
2960STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2961{
2962 CheckComArgOutPointerValid(aPassword);
2963
2964 AutoCaller autoCaller(this);
2965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2966
2967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2970
2971 return S_OK;
2972}
2973
2974STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2975{
2976 AutoCaller autoCaller(this);
2977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2978
2979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 /* @todo deal with running state change. */
2982 HRESULT rc = checkStateDependency(MutableStateDep);
2983 if (FAILED(rc)) return rc;
2984
2985 setModified(IsModified_MachineData);
2986 mUserData.backup();
2987 mUserData->s.strFaultTolerancePassword = aPassword;
2988
2989 return S_OK;
2990}
2991
2992STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2993{
2994 CheckComArgOutPointerValid(aInterval);
2995
2996 AutoCaller autoCaller(this);
2997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2998
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 *aInterval = mUserData->s.uFaultToleranceInterval;
3002 return S_OK;
3003}
3004
3005STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3006{
3007 AutoCaller autoCaller(this);
3008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3009
3010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 /* @todo deal with running state change. */
3013 HRESULT rc = checkStateDependency(MutableStateDep);
3014 if (FAILED(rc)) return rc;
3015
3016 setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.uFaultToleranceInterval = aInterval;
3019 return S_OK;
3020}
3021
3022STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3023{
3024 CheckComArgOutPointerValid(aEnabled);
3025
3026 AutoCaller autoCaller(this);
3027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3028
3029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3030
3031 *aEnabled = mUserData->s.fRTCUseUTC;
3032
3033 return S_OK;
3034}
3035
3036STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3037{
3038 AutoCaller autoCaller(this);
3039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3040
3041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 /* Only allow it to be set to true when PoweredOff or Aborted.
3044 (Clearing it is always permitted.) */
3045 if ( aEnabled
3046 && mData->mRegistered
3047 && ( !isSessionMachine()
3048 || ( mData->mMachineState != MachineState_PoweredOff
3049 && mData->mMachineState != MachineState_Teleported
3050 && mData->mMachineState != MachineState_Aborted
3051 )
3052 )
3053 )
3054 return setError(VBOX_E_INVALID_VM_STATE,
3055 tr("The machine is not powered off (state is %s)"),
3056 Global::stringifyMachineState(mData->mMachineState));
3057
3058 setModified(IsModified_MachineData);
3059 mUserData.backup();
3060 mUserData->s.fRTCUseUTC = !!aEnabled;
3061
3062 return S_OK;
3063}
3064
3065STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
3066{
3067 CheckComArgOutPointerValid(aEnabled);
3068
3069 AutoCaller autoCaller(this);
3070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3071
3072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3073
3074 *aEnabled = mHWData->mIoCacheEnabled;
3075
3076 return S_OK;
3077}
3078
3079STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
3080{
3081 AutoCaller autoCaller(this);
3082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3083
3084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3085
3086 HRESULT rc = checkStateDependency(MutableStateDep);
3087 if (FAILED(rc)) return rc;
3088
3089 setModified(IsModified_MachineData);
3090 mHWData.backup();
3091 mHWData->mIoCacheEnabled = aEnabled;
3092
3093 return S_OK;
3094}
3095
3096STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
3097{
3098 CheckComArgOutPointerValid(aIoCacheSize);
3099
3100 AutoCaller autoCaller(this);
3101 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3102
3103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 *aIoCacheSize = mHWData->mIoCacheSize;
3106
3107 return S_OK;
3108}
3109
3110STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
3111{
3112 AutoCaller autoCaller(this);
3113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3114
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 HRESULT rc = checkStateDependency(MutableStateDep);
3118 if (FAILED(rc)) return rc;
3119
3120 setModified(IsModified_MachineData);
3121 mHWData.backup();
3122 mHWData->mIoCacheSize = aIoCacheSize;
3123
3124 return S_OK;
3125}
3126
3127
3128/**
3129 * @note Locks objects!
3130 */
3131STDMETHODIMP Machine::LockMachine(ISession *aSession,
3132 LockType_T lockType)
3133{
3134 CheckComArgNotNull(aSession);
3135
3136 AutoCaller autoCaller(this);
3137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3138
3139 /* check the session state */
3140 SessionState_T state;
3141 HRESULT rc = aSession->COMGETTER(State)(&state);
3142 if (FAILED(rc)) return rc;
3143
3144 if (state != SessionState_Unlocked)
3145 return setError(VBOX_E_INVALID_OBJECT_STATE,
3146 tr("The given session is busy"));
3147
3148 // get the client's IInternalSessionControl interface
3149 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3150 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3151 E_INVALIDARG);
3152
3153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3154
3155 if (!mData->mRegistered)
3156 return setError(E_UNEXPECTED,
3157 tr("The machine '%s' is not registered"),
3158 mUserData->s.strName.c_str());
3159
3160 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3161
3162 SessionState_T oldState = mData->mSession.mState;
3163 /* Hack: in case the session is closing and there is a progress object
3164 * which allows waiting for the session to be closed, take the opportunity
3165 * and do a limited wait (max. 1 second). This helps a lot when the system
3166 * is busy and thus session closing can take a little while. */
3167 if ( mData->mSession.mState == SessionState_Unlocking
3168 && mData->mSession.mProgress)
3169 {
3170 alock.release();
3171 mData->mSession.mProgress->WaitForCompletion(1000);
3172 alock.acquire();
3173 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3174 }
3175
3176 // try again now
3177 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3178 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3179 )
3180 {
3181 // OK, share the session... we are now dealing with three processes:
3182 // 1) VBoxSVC (where this code runs);
3183 // 2) process C: the caller's client process (who wants a shared session);
3184 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3185
3186 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3187 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3188 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3189 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3190 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3191
3192 /*
3193 * Release the lock before calling the client process. It's safe here
3194 * since the only thing to do after we get the lock again is to add
3195 * the remote control to the list (which doesn't directly influence
3196 * anything).
3197 */
3198 alock.release();
3199
3200 // get the console of the session holding the write lock (this is a remote call)
3201 ComPtr<IConsole> pConsoleW;
3202 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3203 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3204 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3205 if (FAILED(rc))
3206 // the failure may occur w/o any error info (from RPC), so provide one
3207 return setError(VBOX_E_VM_ERROR,
3208 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3209
3210 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3211
3212 // share the session machine and W's console with the caller's session
3213 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3214 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3215 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3216
3217 if (FAILED(rc))
3218 // the failure may occur w/o any error info (from RPC), so provide one
3219 return setError(VBOX_E_VM_ERROR,
3220 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3221 alock.acquire();
3222
3223 // need to revalidate the state after acquiring the lock again
3224 if (mData->mSession.mState != SessionState_Locked)
3225 {
3226 pSessionControl->Uninitialize();
3227 return setError(VBOX_E_INVALID_SESSION_STATE,
3228 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3229 mUserData->s.strName.c_str());
3230 }
3231
3232 // add the caller's session to the list
3233 mData->mSession.mRemoteControls.push_back(pSessionControl);
3234 }
3235 else if ( mData->mSession.mState == SessionState_Locked
3236 || mData->mSession.mState == SessionState_Unlocking
3237 )
3238 {
3239 // sharing not permitted, or machine still unlocking:
3240 return setError(VBOX_E_INVALID_OBJECT_STATE,
3241 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3242 mUserData->s.strName.c_str());
3243 }
3244 else
3245 {
3246 // machine is not locked: then write-lock the machine (create the session machine)
3247
3248 // must not be busy
3249 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3250
3251 // get the caller's session PID
3252 RTPROCESS pid = NIL_RTPROCESS;
3253 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3254 pSessionControl->GetPID((ULONG*)&pid);
3255 Assert(pid != NIL_RTPROCESS);
3256
3257 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3258
3259 if (fLaunchingVMProcess)
3260 {
3261 // this machine is awaiting for a spawning session to be opened:
3262 // then the calling process must be the one that got started by
3263 // LaunchVMProcess()
3264
3265 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
3266 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3267
3268 if (mData->mSession.mPid != pid)
3269 return setError(E_ACCESSDENIED,
3270 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3271 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3272 pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
3273 }
3274
3275 // create the mutable SessionMachine from the current machine
3276 ComObjPtr<SessionMachine> sessionMachine;
3277 sessionMachine.createObject();
3278 rc = sessionMachine->init(this);
3279 AssertComRC(rc);
3280
3281 /* NOTE: doing return from this function after this point but
3282 * before the end is forbidden since it may call SessionMachine::uninit()
3283 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3284 * lock while still holding the Machine lock in alock so that a deadlock
3285 * is possible due to the wrong lock order. */
3286
3287 if (SUCCEEDED(rc))
3288 {
3289 /*
3290 * Set the session state to Spawning to protect against subsequent
3291 * attempts to open a session and to unregister the machine after
3292 * we release the lock.
3293 */
3294 SessionState_T origState = mData->mSession.mState;
3295 mData->mSession.mState = SessionState_Spawning;
3296
3297 /*
3298 * Release the lock before calling the client process -- it will call
3299 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3300 * because the state is Spawning, so that LaunchVMProcess() and
3301 * LockMachine() calls will fail. This method, called before we
3302 * acquire the lock again, will fail because of the wrong PID.
3303 *
3304 * Note that mData->mSession.mRemoteControls accessed outside
3305 * the lock may not be modified when state is Spawning, so it's safe.
3306 */
3307 alock.release();
3308
3309 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3310 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3311 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3312
3313 /* The failure may occur w/o any error info (from RPC), so provide one */
3314 if (FAILED(rc))
3315 setError(VBOX_E_VM_ERROR,
3316 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3317
3318 if ( SUCCEEDED(rc)
3319 && fLaunchingVMProcess
3320 )
3321 {
3322 /* complete the remote session initialization */
3323
3324 /* get the console from the direct session */
3325 ComPtr<IConsole> console;
3326 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3327 ComAssertComRC(rc);
3328
3329 if (SUCCEEDED(rc) && !console)
3330 {
3331 ComAssert(!!console);
3332 rc = E_FAIL;
3333 }
3334
3335 /* assign machine & console to the remote session */
3336 if (SUCCEEDED(rc))
3337 {
3338 /*
3339 * after LaunchVMProcess(), the first and the only
3340 * entry in remoteControls is that remote session
3341 */
3342 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3343 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3344 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3345
3346 /* The failure may occur w/o any error info (from RPC), so provide one */
3347 if (FAILED(rc))
3348 setError(VBOX_E_VM_ERROR,
3349 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3350 }
3351
3352 if (FAILED(rc))
3353 pSessionControl->Uninitialize();
3354 }
3355
3356 /* acquire the lock again */
3357 alock.acquire();
3358
3359 /* Restore the session state */
3360 mData->mSession.mState = origState;
3361 }
3362
3363 // finalize spawning anyway (this is why we don't return on errors above)
3364 if (fLaunchingVMProcess)
3365 {
3366 /* Note that the progress object is finalized later */
3367 /** @todo Consider checking mData->mSession.mProgress for cancellation
3368 * around here. */
3369
3370 /* We don't reset mSession.mPid here because it is necessary for
3371 * SessionMachine::uninit() to reap the child process later. */
3372
3373 if (FAILED(rc))
3374 {
3375 /* Close the remote session, remove the remote control from the list
3376 * and reset session state to Closed (@note keep the code in sync
3377 * with the relevant part in openSession()). */
3378
3379 Assert(mData->mSession.mRemoteControls.size() == 1);
3380 if (mData->mSession.mRemoteControls.size() == 1)
3381 {
3382 ErrorInfoKeeper eik;
3383 mData->mSession.mRemoteControls.front()->Uninitialize();
3384 }
3385
3386 mData->mSession.mRemoteControls.clear();
3387 mData->mSession.mState = SessionState_Unlocked;
3388 }
3389 }
3390 else
3391 {
3392 /* memorize PID of the directly opened session */
3393 if (SUCCEEDED(rc))
3394 mData->mSession.mPid = pid;
3395 }
3396
3397 if (SUCCEEDED(rc))
3398 {
3399 /* memorize the direct session control and cache IUnknown for it */
3400 mData->mSession.mDirectControl = pSessionControl;
3401 mData->mSession.mState = SessionState_Locked;
3402 /* associate the SessionMachine with this Machine */
3403 mData->mSession.mMachine = sessionMachine;
3404
3405 /* request an IUnknown pointer early from the remote party for later
3406 * identity checks (it will be internally cached within mDirectControl
3407 * at least on XPCOM) */
3408 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3409 NOREF(unk);
3410 }
3411
3412 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3413 * would break the lock order */
3414 alock.release();
3415
3416 /* uninitialize the created session machine on failure */
3417 if (FAILED(rc))
3418 sessionMachine->uninit();
3419
3420 }
3421
3422 if (SUCCEEDED(rc))
3423 {
3424 /*
3425 * tell the client watcher thread to update the set of
3426 * machines that have open sessions
3427 */
3428 mParent->updateClientWatcher();
3429
3430 if (oldState != SessionState_Locked)
3431 /* fire an event */
3432 mParent->onSessionStateChange(getId(), SessionState_Locked);
3433 }
3434
3435 return rc;
3436}
3437
3438/**
3439 * @note Locks objects!
3440 */
3441STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3442 IN_BSTR aType,
3443 IN_BSTR aEnvironment,
3444 IProgress **aProgress)
3445{
3446 CheckComArgStrNotEmptyOrNull(aType);
3447 Utf8Str strType(aType);
3448 Utf8Str strEnvironment(aEnvironment);
3449 /* "emergencystop" doesn't need the session, so skip the checks/interface
3450 * retrieval. This code doesn't quite fit in here, but introducing a
3451 * special API method would be even more effort, and would require explicit
3452 * support by every API client. It's better to hide the feature a bit. */
3453 if (strType != "emergencystop")
3454 CheckComArgNotNull(aSession);
3455 CheckComArgOutPointerValid(aProgress);
3456
3457 AutoCaller autoCaller(this);
3458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3459
3460 ComPtr<IInternalSessionControl> control;
3461 HRESULT rc = S_OK;
3462
3463 if (strType != "emergencystop")
3464 {
3465 /* check the session state */
3466 SessionState_T state;
3467 rc = aSession->COMGETTER(State)(&state);
3468 if (FAILED(rc))
3469 return rc;
3470
3471 if (state != SessionState_Unlocked)
3472 return setError(VBOX_E_INVALID_OBJECT_STATE,
3473 tr("The given session is busy"));
3474
3475 /* get the IInternalSessionControl interface */
3476 control = aSession;
3477 ComAssertMsgRet(!control.isNull(),
3478 ("No IInternalSessionControl interface"),
3479 E_INVALIDARG);
3480 }
3481
3482 /* get the teleporter enable state for the progress object init. */
3483 BOOL fTeleporterEnabled;
3484 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3485 if (FAILED(rc))
3486 return rc;
3487
3488 /* create a progress object */
3489 if (strType != "emergencystop")
3490 {
3491 ComObjPtr<ProgressProxy> progress;
3492 progress.createObject();
3493 rc = progress->init(mParent,
3494 static_cast<IMachine*>(this),
3495 Bstr(tr("Starting VM")).raw(),
3496 TRUE /* aCancelable */,
3497 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3498 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3499 2 /* uFirstOperationWeight */,
3500 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3501
3502 if (SUCCEEDED(rc))
3503 {
3504 rc = launchVMProcess(control, strType, strEnvironment, progress);
3505 if (SUCCEEDED(rc))
3506 {
3507 progress.queryInterfaceTo(aProgress);
3508
3509 /* signal the client watcher thread */
3510 mParent->updateClientWatcher();
3511
3512 /* fire an event */
3513 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3514 }
3515 }
3516 }
3517 else
3518 {
3519 /* no progress object - either instant success or failure */
3520 *aProgress = NULL;
3521
3522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3523
3524 if (mData->mSession.mState != SessionState_Locked)
3525 return setError(VBOX_E_INVALID_OBJECT_STATE,
3526 tr("The machine '%s' is not locked by a session"),
3527 mUserData->s.strName.c_str());
3528
3529 /* must have a VM process associated - do not kill normal API clients
3530 * with an open session */
3531 if (!Global::IsOnline(mData->mMachineState))
3532 return setError(VBOX_E_INVALID_OBJECT_STATE,
3533 tr("The machine '%s' does not have a VM process"),
3534 mUserData->s.strName.c_str());
3535
3536 /* forcibly terminate the VM process */
3537 if (mData->mSession.mPid != NIL_RTPROCESS)
3538 RTProcTerminate(mData->mSession.mPid);
3539
3540 /* signal the client watcher thread, as most likely the client has
3541 * been terminated */
3542 mParent->updateClientWatcher();
3543 }
3544
3545 return rc;
3546}
3547
3548STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3549{
3550 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3551 return setError(E_INVALIDARG,
3552 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3553 aPosition, SchemaDefs::MaxBootPosition);
3554
3555 if (aDevice == DeviceType_USB)
3556 return setError(E_NOTIMPL,
3557 tr("Booting from USB device is currently not supported"));
3558
3559 AutoCaller autoCaller(this);
3560 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3561
3562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3563
3564 HRESULT rc = checkStateDependency(MutableStateDep);
3565 if (FAILED(rc)) return rc;
3566
3567 setModified(IsModified_MachineData);
3568 mHWData.backup();
3569 mHWData->mBootOrder[aPosition - 1] = aDevice;
3570
3571 return S_OK;
3572}
3573
3574STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3575{
3576 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3577 return setError(E_INVALIDARG,
3578 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3579 aPosition, SchemaDefs::MaxBootPosition);
3580
3581 AutoCaller autoCaller(this);
3582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3583
3584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3585
3586 *aDevice = mHWData->mBootOrder[aPosition - 1];
3587
3588 return S_OK;
3589}
3590
3591STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3592 LONG aControllerPort,
3593 LONG aDevice,
3594 DeviceType_T aType,
3595 IMedium *aMedium)
3596{
3597 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3598 aControllerName, aControllerPort, aDevice, aType, aMedium));
3599
3600 CheckComArgStrNotEmptyOrNull(aControllerName);
3601
3602 AutoCaller autoCaller(this);
3603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3604
3605 // request the host lock first, since might be calling Host methods for getting host drives;
3606 // next, protect the media tree all the while we're in here, as well as our member variables
3607 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3608 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3609
3610 HRESULT rc = checkStateDependency(MutableStateDep);
3611 if (FAILED(rc)) return rc;
3612
3613 /// @todo NEWMEDIA implicit machine registration
3614 if (!mData->mRegistered)
3615 return setError(VBOX_E_INVALID_OBJECT_STATE,
3616 tr("Cannot attach storage devices to an unregistered machine"));
3617
3618 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3619
3620 /* Check for an existing controller. */
3621 ComObjPtr<StorageController> ctl;
3622 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3623 if (FAILED(rc)) return rc;
3624
3625 StorageControllerType_T ctrlType;
3626 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3627 if (FAILED(rc))
3628 return setError(E_FAIL,
3629 tr("Could not get type of controller '%ls'"),
3630 aControllerName);
3631
3632 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3633 bool fHotplug = false;
3634 if (Global::IsOnlineOrTransient(mData->mMachineState))
3635 fHotplug = true;
3636
3637 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3638 return setError(VBOX_E_INVALID_VM_STATE,
3639 tr("Controller '%ls' does not support hotplugging"),
3640 aControllerName);
3641
3642 // check that the port and device are not out of range
3643 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3644 if (FAILED(rc)) return rc;
3645
3646 /* check if the device slot is already busy */
3647 MediumAttachment *pAttachTemp;
3648 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3649 aControllerName,
3650 aControllerPort,
3651 aDevice)))
3652 {
3653 Medium *pMedium = pAttachTemp->getMedium();
3654 if (pMedium)
3655 {
3656 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3657 return setError(VBOX_E_OBJECT_IN_USE,
3658 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3659 pMedium->getLocationFull().c_str(),
3660 aControllerPort,
3661 aDevice,
3662 aControllerName);
3663 }
3664 else
3665 return setError(VBOX_E_OBJECT_IN_USE,
3666 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3667 aControllerPort, aDevice, aControllerName);
3668 }
3669
3670 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3671 if (aMedium && medium.isNull())
3672 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3673
3674 AutoCaller mediumCaller(medium);
3675 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3676
3677 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3678
3679 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3680 && !medium.isNull()
3681 )
3682 return setError(VBOX_E_OBJECT_IN_USE,
3683 tr("Medium '%s' is already attached to this virtual machine"),
3684 medium->getLocationFull().c_str());
3685
3686 if (!medium.isNull())
3687 {
3688 MediumType_T mtype = medium->getType();
3689 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3690 // For DVDs it's not written to the config file, so needs no global config
3691 // version bump. For floppies it's a new attribute "type", which is ignored
3692 // by older VirtualBox version, so needs no global config version bump either.
3693 // For hard disks this type is not accepted.
3694 if (mtype == MediumType_MultiAttach)
3695 {
3696 // This type is new with VirtualBox 4.0 and therefore requires settings
3697 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3698 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3699 // two reasons: The medium type is a property of the media registry tree, which
3700 // can reside in the global config file (for pre-4.0 media); we would therefore
3701 // possibly need to bump the global config version. We don't want to do that though
3702 // because that might make downgrading to pre-4.0 impossible.
3703 // As a result, we can only use these two new types if the medium is NOT in the
3704 // global registry:
3705 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3706 if ( medium->isInRegistry(uuidGlobalRegistry)
3707 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3708 )
3709 return setError(VBOX_E_INVALID_OBJECT_STATE,
3710 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3711 "to machines that were created with VirtualBox 4.0 or later"),
3712 medium->getLocationFull().c_str());
3713 }
3714 }
3715
3716 bool fIndirect = false;
3717 if (!medium.isNull())
3718 fIndirect = medium->isReadOnly();
3719 bool associate = true;
3720
3721 do
3722 {
3723 if ( aType == DeviceType_HardDisk
3724 && mMediaData.isBackedUp())
3725 {
3726 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3727
3728 /* check if the medium was attached to the VM before we started
3729 * changing attachments in which case the attachment just needs to
3730 * be restored */
3731 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3732 {
3733 AssertReturn(!fIndirect, E_FAIL);
3734
3735 /* see if it's the same bus/channel/device */
3736 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3737 {
3738 /* the simplest case: restore the whole attachment
3739 * and return, nothing else to do */
3740 mMediaData->mAttachments.push_back(pAttachTemp);
3741 return S_OK;
3742 }
3743
3744 /* bus/channel/device differ; we need a new attachment object,
3745 * but don't try to associate it again */
3746 associate = false;
3747 break;
3748 }
3749 }
3750
3751 /* go further only if the attachment is to be indirect */
3752 if (!fIndirect)
3753 break;
3754
3755 /* perform the so called smart attachment logic for indirect
3756 * attachments. Note that smart attachment is only applicable to base
3757 * hard disks. */
3758
3759 if (medium->getParent().isNull())
3760 {
3761 /* first, investigate the backup copy of the current hard disk
3762 * attachments to make it possible to re-attach existing diffs to
3763 * another device slot w/o losing their contents */
3764 if (mMediaData.isBackedUp())
3765 {
3766 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3767
3768 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3769 uint32_t foundLevel = 0;
3770
3771 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3772 it != oldAtts.end();
3773 ++it)
3774 {
3775 uint32_t level = 0;
3776 MediumAttachment *pAttach = *it;
3777 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3778 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3779 if (pMedium.isNull())
3780 continue;
3781
3782 if (pMedium->getBase(&level) == medium)
3783 {
3784 /* skip the hard disk if its currently attached (we
3785 * cannot attach the same hard disk twice) */
3786 if (findAttachment(mMediaData->mAttachments,
3787 pMedium))
3788 continue;
3789
3790 /* matched device, channel and bus (i.e. attached to the
3791 * same place) will win and immediately stop the search;
3792 * otherwise the attachment that has the youngest
3793 * descendant of medium will be used
3794 */
3795 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3796 {
3797 /* the simplest case: restore the whole attachment
3798 * and return, nothing else to do */
3799 mMediaData->mAttachments.push_back(*it);
3800 return S_OK;
3801 }
3802 else if ( foundIt == oldAtts.end()
3803 || level > foundLevel /* prefer younger */
3804 )
3805 {
3806 foundIt = it;
3807 foundLevel = level;
3808 }
3809 }
3810 }
3811
3812 if (foundIt != oldAtts.end())
3813 {
3814 /* use the previously attached hard disk */
3815 medium = (*foundIt)->getMedium();
3816 mediumCaller.attach(medium);
3817 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3818 mediumLock.attach(medium);
3819 /* not implicit, doesn't require association with this VM */
3820 fIndirect = false;
3821 associate = false;
3822 /* go right to the MediumAttachment creation */
3823 break;
3824 }
3825 }
3826
3827 /* must give up the medium lock and medium tree lock as below we
3828 * go over snapshots, which needs a lock with higher lock order. */
3829 mediumLock.release();
3830 treeLock.release();
3831
3832 /* then, search through snapshots for the best diff in the given
3833 * hard disk's chain to base the new diff on */
3834
3835 ComObjPtr<Medium> base;
3836 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3837 while (snap)
3838 {
3839 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3840
3841 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3842
3843 MediumAttachment *pAttachFound = NULL;
3844 uint32_t foundLevel = 0;
3845
3846 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3847 it != snapAtts.end();
3848 ++it)
3849 {
3850 MediumAttachment *pAttach = *it;
3851 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3852 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3853 if (pMedium.isNull())
3854 continue;
3855
3856 uint32_t level = 0;
3857 if (pMedium->getBase(&level) == medium)
3858 {
3859 /* matched device, channel and bus (i.e. attached to the
3860 * same place) will win and immediately stop the search;
3861 * otherwise the attachment that has the youngest
3862 * descendant of medium will be used
3863 */
3864 if ( pAttach->getDevice() == aDevice
3865 && pAttach->getPort() == aControllerPort
3866 && pAttach->getControllerName() == aControllerName
3867 )
3868 {
3869 pAttachFound = pAttach;
3870 break;
3871 }
3872 else if ( !pAttachFound
3873 || level > foundLevel /* prefer younger */
3874 )
3875 {
3876 pAttachFound = pAttach;
3877 foundLevel = level;
3878 }
3879 }
3880 }
3881
3882 if (pAttachFound)
3883 {
3884 base = pAttachFound->getMedium();
3885 break;
3886 }
3887
3888 snap = snap->getParent();
3889 }
3890
3891 /* re-lock medium tree and the medium, as we need it below */
3892 treeLock.acquire();
3893 mediumLock.acquire();
3894
3895 /* found a suitable diff, use it as a base */
3896 if (!base.isNull())
3897 {
3898 medium = base;
3899 mediumCaller.attach(medium);
3900 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3901 mediumLock.attach(medium);
3902 }
3903 }
3904
3905 Utf8Str strFullSnapshotFolder;
3906 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3907
3908 ComObjPtr<Medium> diff;
3909 diff.createObject();
3910 // store this diff in the same registry as the parent
3911 Guid uuidRegistryParent;
3912 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
3913 {
3914 // parent image has no registry: this can happen if we're attaching a new immutable
3915 // image that has not yet been attached (medium then points to the base and we're
3916 // creating the diff image for the immutable, and the parent is not yet registered);
3917 // put the parent in the machine registry then
3918 mediumLock.release();
3919 treeLock.release();
3920 alock.release();
3921 addMediumToRegistry(medium);
3922 alock.acquire();
3923 treeLock.acquire();
3924 mediumLock.acquire();
3925 medium->getFirstRegistryMachineId(uuidRegistryParent);
3926 }
3927 rc = diff->init(mParent,
3928 medium->getPreferredDiffFormat(),
3929 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3930 uuidRegistryParent);
3931 if (FAILED(rc)) return rc;
3932
3933 /* Apply the normal locking logic to the entire chain. */
3934 MediumLockList *pMediumLockList(new MediumLockList());
3935 mediumLock.release();
3936 treeLock.release();
3937 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3938 true /* fMediumLockWrite */,
3939 medium,
3940 *pMediumLockList);
3941 treeLock.acquire();
3942 mediumLock.acquire();
3943 if (SUCCEEDED(rc))
3944 {
3945 mediumLock.release();
3946 treeLock.release();
3947 rc = pMediumLockList->Lock();
3948 treeLock.acquire();
3949 mediumLock.acquire();
3950 if (FAILED(rc))
3951 setError(rc,
3952 tr("Could not lock medium when creating diff '%s'"),
3953 diff->getLocationFull().c_str());
3954 else
3955 {
3956 /* will release the lock before the potentially lengthy
3957 * operation, so protect with the special state */
3958 MachineState_T oldState = mData->mMachineState;
3959 setMachineState(MachineState_SettingUp);
3960
3961 mediumLock.release();
3962 treeLock.release();
3963 alock.release();
3964
3965 rc = medium->createDiffStorage(diff,
3966 MediumVariant_Standard,
3967 pMediumLockList,
3968 NULL /* aProgress */,
3969 true /* aWait */);
3970
3971 alock.acquire();
3972 treeLock.acquire();
3973 mediumLock.acquire();
3974
3975 setMachineState(oldState);
3976 }
3977 }
3978
3979 /* Unlock the media and free the associated memory. */
3980 delete pMediumLockList;
3981
3982 if (FAILED(rc)) return rc;
3983
3984 /* use the created diff for the actual attachment */
3985 medium = diff;
3986 mediumCaller.attach(medium);
3987 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3988 mediumLock.attach(medium);
3989 }
3990 while (0);
3991
3992 ComObjPtr<MediumAttachment> attachment;
3993 attachment.createObject();
3994 rc = attachment->init(this,
3995 medium,
3996 aControllerName,
3997 aControllerPort,
3998 aDevice,
3999 aType,
4000 fIndirect,
4001 false /* fPassthrough */,
4002 false /* fTempEject */,
4003 false /* fNonRotational */,
4004 false /* fDiscard */,
4005 Utf8Str::Empty);
4006 if (FAILED(rc)) return rc;
4007
4008 if (associate && !medium.isNull())
4009 {
4010 // as the last step, associate the medium to the VM
4011 rc = medium->addBackReference(mData->mUuid);
4012 // here we can fail because of Deleting, or being in process of creating a Diff
4013 if (FAILED(rc)) return rc;
4014
4015 mediumLock.release();
4016 treeLock.release();
4017 alock.release();
4018 addMediumToRegistry(medium);
4019 alock.acquire();
4020 treeLock.acquire();
4021 mediumLock.acquire();
4022 }
4023
4024 /* success: finally remember the attachment */
4025 setModified(IsModified_Storage);
4026 mMediaData.backup();
4027 mMediaData->mAttachments.push_back(attachment);
4028
4029 mediumLock.release();
4030 treeLock.release();
4031 alock.release();
4032
4033 if (fHotplug)
4034 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4035
4036 mParent->saveModifiedRegistries();
4037
4038 return rc;
4039}
4040
4041STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4042 LONG aDevice)
4043{
4044 CheckComArgStrNotEmptyOrNull(aControllerName);
4045
4046 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4047 aControllerName, aControllerPort, aDevice));
4048
4049 AutoCaller autoCaller(this);
4050 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4051
4052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4053
4054 HRESULT rc = checkStateDependency(MutableStateDep);
4055 if (FAILED(rc)) return rc;
4056
4057 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4058
4059 /* Check for an existing controller. */
4060 ComObjPtr<StorageController> ctl;
4061 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4062 if (FAILED(rc)) return rc;
4063
4064 StorageControllerType_T ctrlType;
4065 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4066 if (FAILED(rc))
4067 return setError(E_FAIL,
4068 tr("Could not get type of controller '%ls'"),
4069 aControllerName);
4070
4071 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4072 bool fHotplug = false;
4073 if (Global::IsOnlineOrTransient(mData->mMachineState))
4074 fHotplug = true;
4075
4076 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4077 return setError(VBOX_E_INVALID_VM_STATE,
4078 tr("Controller '%ls' does not support hotplugging"),
4079 aControllerName);
4080
4081 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4082 aControllerName,
4083 aControllerPort,
4084 aDevice);
4085 if (!pAttach)
4086 return setError(VBOX_E_OBJECT_NOT_FOUND,
4087 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4088 aDevice, aControllerPort, aControllerName);
4089
4090 /*
4091 * The VM has to detach the device before we delete any implicit diffs.
4092 * If this fails we can roll back without loosing data.
4093 */
4094 if (fHotplug)
4095 {
4096 alock.release();
4097 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4098 alock.acquire();
4099 }
4100 if (FAILED(rc)) return rc;
4101
4102 /* If we are here everything went well and we can delete the implicit now. */
4103 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4104
4105 alock.release();
4106
4107 mParent->saveModifiedRegistries();
4108
4109 return rc;
4110}
4111
4112STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4113 LONG aDevice, BOOL aPassthrough)
4114{
4115 CheckComArgStrNotEmptyOrNull(aControllerName);
4116
4117 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4118 aControllerName, aControllerPort, aDevice, aPassthrough));
4119
4120 AutoCaller autoCaller(this);
4121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4122
4123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4124
4125 HRESULT rc = checkStateDependency(MutableStateDep);
4126 if (FAILED(rc)) return rc;
4127
4128 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4129
4130 if (Global::IsOnlineOrTransient(mData->mMachineState))
4131 return setError(VBOX_E_INVALID_VM_STATE,
4132 tr("Invalid machine state: %s"),
4133 Global::stringifyMachineState(mData->mMachineState));
4134
4135 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4136 aControllerName,
4137 aControllerPort,
4138 aDevice);
4139 if (!pAttach)
4140 return setError(VBOX_E_OBJECT_NOT_FOUND,
4141 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4142 aDevice, aControllerPort, aControllerName);
4143
4144
4145 setModified(IsModified_Storage);
4146 mMediaData.backup();
4147
4148 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4149
4150 if (pAttach->getType() != DeviceType_DVD)
4151 return setError(E_INVALIDARG,
4152 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4153 aDevice, aControllerPort, aControllerName);
4154 pAttach->updatePassthrough(!!aPassthrough);
4155
4156 return S_OK;
4157}
4158
4159STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4160 LONG aDevice, BOOL aTemporaryEject)
4161{
4162 CheckComArgStrNotEmptyOrNull(aControllerName);
4163
4164 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4165 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4166
4167 AutoCaller autoCaller(this);
4168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4169
4170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4171
4172 HRESULT rc = checkStateDependency(MutableStateDep);
4173 if (FAILED(rc)) return rc;
4174
4175 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4176 aControllerName,
4177 aControllerPort,
4178 aDevice);
4179 if (!pAttach)
4180 return setError(VBOX_E_OBJECT_NOT_FOUND,
4181 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4182 aDevice, aControllerPort, aControllerName);
4183
4184
4185 setModified(IsModified_Storage);
4186 mMediaData.backup();
4187
4188 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4189
4190 if (pAttach->getType() != DeviceType_DVD)
4191 return setError(E_INVALIDARG,
4192 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4193 aDevice, aControllerPort, aControllerName);
4194 pAttach->updateTempEject(!!aTemporaryEject);
4195
4196 return S_OK;
4197}
4198
4199STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4200 LONG aDevice, BOOL aNonRotational)
4201{
4202 CheckComArgStrNotEmptyOrNull(aControllerName);
4203
4204 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4205 aControllerName, aControllerPort, aDevice, aNonRotational));
4206
4207 AutoCaller autoCaller(this);
4208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4209
4210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4211
4212 HRESULT rc = checkStateDependency(MutableStateDep);
4213 if (FAILED(rc)) return rc;
4214
4215 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4216
4217 if (Global::IsOnlineOrTransient(mData->mMachineState))
4218 return setError(VBOX_E_INVALID_VM_STATE,
4219 tr("Invalid machine state: %s"),
4220 Global::stringifyMachineState(mData->mMachineState));
4221
4222 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4223 aControllerName,
4224 aControllerPort,
4225 aDevice);
4226 if (!pAttach)
4227 return setError(VBOX_E_OBJECT_NOT_FOUND,
4228 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4229 aDevice, aControllerPort, aControllerName);
4230
4231
4232 setModified(IsModified_Storage);
4233 mMediaData.backup();
4234
4235 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4236
4237 if (pAttach->getType() != DeviceType_HardDisk)
4238 return setError(E_INVALIDARG,
4239 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"),
4240 aDevice, aControllerPort, aControllerName);
4241 pAttach->updateNonRotational(!!aNonRotational);
4242
4243 return S_OK;
4244}
4245
4246STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4247 LONG aDevice, BOOL aDiscard)
4248{
4249 CheckComArgStrNotEmptyOrNull(aControllerName);
4250
4251 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4252 aControllerName, aControllerPort, aDevice, aDiscard));
4253
4254 AutoCaller autoCaller(this);
4255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4256
4257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4258
4259 HRESULT rc = checkStateDependency(MutableStateDep);
4260 if (FAILED(rc)) return rc;
4261
4262 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4263
4264 if (Global::IsOnlineOrTransient(mData->mMachineState))
4265 return setError(VBOX_E_INVALID_VM_STATE,
4266 tr("Invalid machine state: %s"),
4267 Global::stringifyMachineState(mData->mMachineState));
4268
4269 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4270 aControllerName,
4271 aControllerPort,
4272 aDevice);
4273 if (!pAttach)
4274 return setError(VBOX_E_OBJECT_NOT_FOUND,
4275 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4276 aDevice, aControllerPort, aControllerName);
4277
4278
4279 setModified(IsModified_Storage);
4280 mMediaData.backup();
4281
4282 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4283
4284 if (pAttach->getType() != DeviceType_HardDisk)
4285 return setError(E_INVALIDARG,
4286 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"),
4287 aDevice, aControllerPort, aControllerName);
4288 pAttach->updateDiscard(!!aDiscard);
4289
4290 return S_OK;
4291}
4292
4293STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4294 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4295{
4296 CheckComArgStrNotEmptyOrNull(aControllerName);
4297
4298 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4299 aControllerName, aControllerPort, aDevice));
4300
4301 AutoCaller autoCaller(this);
4302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4303
4304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4305
4306 HRESULT rc = checkStateDependency(MutableStateDep);
4307 if (FAILED(rc)) return rc;
4308
4309 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4310
4311 if (Global::IsOnlineOrTransient(mData->mMachineState))
4312 return setError(VBOX_E_INVALID_VM_STATE,
4313 tr("Invalid machine state: %s"),
4314 Global::stringifyMachineState(mData->mMachineState));
4315
4316 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4317 aControllerName,
4318 aControllerPort,
4319 aDevice);
4320 if (!pAttach)
4321 return setError(VBOX_E_OBJECT_NOT_FOUND,
4322 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4323 aDevice, aControllerPort, aControllerName);
4324
4325
4326 setModified(IsModified_Storage);
4327 mMediaData.backup();
4328
4329 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4330 if (aBandwidthGroup && group.isNull())
4331 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4336 if (strBandwidthGroupOld.isNotEmpty())
4337 {
4338 /* Get the bandwidth group object and release it - this must not fail. */
4339 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4340 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4341 Assert(SUCCEEDED(rc));
4342
4343 pBandwidthGroupOld->release();
4344 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4345 }
4346
4347 if (!group.isNull())
4348 {
4349 group->reference();
4350 pAttach->updateBandwidthGroup(group->getName());
4351 }
4352
4353 return S_OK;
4354}
4355
4356STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4357 LONG aControllerPort,
4358 LONG aDevice,
4359 BOOL aForce)
4360{
4361 int rc = S_OK;
4362 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4363 aControllerName, aControllerPort, aForce));
4364
4365 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4366
4367 return rc;
4368}
4369
4370STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4371 LONG aControllerPort,
4372 LONG aDevice,
4373 IMedium *aMedium,
4374 BOOL aForce)
4375{
4376 int rc = S_OK;
4377 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4378 aControllerName, aControllerPort, aDevice, aForce));
4379
4380 CheckComArgStrNotEmptyOrNull(aControllerName);
4381
4382 AutoCaller autoCaller(this);
4383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4384
4385 // request the host lock first, since might be calling Host methods for getting host drives;
4386 // next, protect the media tree all the while we're in here, as well as our member variables
4387 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4388 this->lockHandle(),
4389 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4390
4391 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4392 aControllerName,
4393 aControllerPort,
4394 aDevice);
4395 if (pAttach.isNull())
4396 return setError(VBOX_E_OBJECT_NOT_FOUND,
4397 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4398 aDevice, aControllerPort, aControllerName);
4399
4400 /* Remember previously mounted medium. The medium before taking the
4401 * backup is not necessarily the same thing. */
4402 ComObjPtr<Medium> oldmedium;
4403 oldmedium = pAttach->getMedium();
4404
4405 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4406 if (aMedium && pMedium.isNull())
4407 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4408
4409 AutoCaller mediumCaller(pMedium);
4410 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4411
4412 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4413 if (pMedium)
4414 {
4415 DeviceType_T mediumType = pAttach->getType();
4416 switch (mediumType)
4417 {
4418 case DeviceType_DVD:
4419 case DeviceType_Floppy:
4420 break;
4421
4422 default:
4423 return setError(VBOX_E_INVALID_OBJECT_STATE,
4424 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4425 aControllerPort,
4426 aDevice,
4427 aControllerName);
4428 }
4429 }
4430
4431 setModified(IsModified_Storage);
4432 mMediaData.backup();
4433
4434 {
4435 // The backup operation makes the pAttach reference point to the
4436 // old settings. Re-get the correct reference.
4437 pAttach = findAttachment(mMediaData->mAttachments,
4438 aControllerName,
4439 aControllerPort,
4440 aDevice);
4441 if (!oldmedium.isNull())
4442 oldmedium->removeBackReference(mData->mUuid);
4443 if (!pMedium.isNull())
4444 {
4445 pMedium->addBackReference(mData->mUuid);
4446
4447 mediumLock.release();
4448 multiLock.release();
4449 addMediumToRegistry(pMedium);
4450 multiLock.acquire();
4451 mediumLock.acquire();
4452 }
4453
4454 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4455 pAttach->updateMedium(pMedium);
4456 }
4457
4458 setModified(IsModified_Storage);
4459
4460 mediumLock.release();
4461 multiLock.release();
4462 rc = onMediumChange(pAttach, aForce);
4463 multiLock.acquire();
4464 mediumLock.acquire();
4465
4466 /* On error roll back this change only. */
4467 if (FAILED(rc))
4468 {
4469 if (!pMedium.isNull())
4470 pMedium->removeBackReference(mData->mUuid);
4471 pAttach = findAttachment(mMediaData->mAttachments,
4472 aControllerName,
4473 aControllerPort,
4474 aDevice);
4475 /* If the attachment is gone in the meantime, bail out. */
4476 if (pAttach.isNull())
4477 return rc;
4478 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4479 if (!oldmedium.isNull())
4480 oldmedium->addBackReference(mData->mUuid);
4481 pAttach->updateMedium(oldmedium);
4482 }
4483
4484 mediumLock.release();
4485 multiLock.release();
4486
4487 mParent->saveModifiedRegistries();
4488
4489 return rc;
4490}
4491
4492STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4493 LONG aControllerPort,
4494 LONG aDevice,
4495 IMedium **aMedium)
4496{
4497 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4498 aControllerName, aControllerPort, aDevice));
4499
4500 CheckComArgStrNotEmptyOrNull(aControllerName);
4501 CheckComArgOutPointerValid(aMedium);
4502
4503 AutoCaller autoCaller(this);
4504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4505
4506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4507
4508 *aMedium = NULL;
4509
4510 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4511 aControllerName,
4512 aControllerPort,
4513 aDevice);
4514 if (pAttach.isNull())
4515 return setError(VBOX_E_OBJECT_NOT_FOUND,
4516 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4517 aDevice, aControllerPort, aControllerName);
4518
4519 pAttach->getMedium().queryInterfaceTo(aMedium);
4520
4521 return S_OK;
4522}
4523
4524STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4525{
4526 CheckComArgOutPointerValid(port);
4527 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4528
4529 AutoCaller autoCaller(this);
4530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4531
4532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4533
4534 mSerialPorts[slot].queryInterfaceTo(port);
4535
4536 return S_OK;
4537}
4538
4539STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4540{
4541 CheckComArgOutPointerValid(port);
4542 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4543
4544 AutoCaller autoCaller(this);
4545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4546
4547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4548
4549 mParallelPorts[slot].queryInterfaceTo(port);
4550
4551 return S_OK;
4552}
4553
4554STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4555{
4556 CheckComArgOutPointerValid(adapter);
4557 CheckComArgExpr(slot, slot < mNetworkAdapters.size());
4558
4559 AutoCaller autoCaller(this);
4560 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4561
4562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4563
4564 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4565
4566 return S_OK;
4567}
4568
4569STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4570{
4571 CheckComArgOutSafeArrayPointerValid(aKeys);
4572
4573 AutoCaller autoCaller(this);
4574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4575
4576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4577
4578 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4579 int i = 0;
4580 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4581 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4582 ++it, ++i)
4583 {
4584 const Utf8Str &strKey = it->first;
4585 strKey.cloneTo(&saKeys[i]);
4586 }
4587 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4588
4589 return S_OK;
4590 }
4591
4592 /**
4593 * @note Locks this object for reading.
4594 */
4595STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4596 BSTR *aValue)
4597{
4598 CheckComArgStrNotEmptyOrNull(aKey);
4599 CheckComArgOutPointerValid(aValue);
4600
4601 AutoCaller autoCaller(this);
4602 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4603
4604 /* start with nothing found */
4605 Bstr bstrResult("");
4606
4607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4608
4609 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4610 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4611 // found:
4612 bstrResult = it->second; // source is a Utf8Str
4613
4614 /* return the result to caller (may be empty) */
4615 bstrResult.cloneTo(aValue);
4616
4617 return S_OK;
4618}
4619
4620 /**
4621 * @note Locks mParent for writing + this object for writing.
4622 */
4623STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4624{
4625 CheckComArgStrNotEmptyOrNull(aKey);
4626
4627 AutoCaller autoCaller(this);
4628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4629
4630 Utf8Str strKey(aKey);
4631 Utf8Str strValue(aValue);
4632 Utf8Str strOldValue; // empty
4633
4634 // locking note: we only hold the read lock briefly to look up the old value,
4635 // then release it and call the onExtraCanChange callbacks. There is a small
4636 // chance of a race insofar as the callback might be called twice if two callers
4637 // change the same key at the same time, but that's a much better solution
4638 // than the deadlock we had here before. The actual changing of the extradata
4639 // is then performed under the write lock and race-free.
4640
4641 // look up the old value first; if nothing has changed then we need not do anything
4642 {
4643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4644 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4645 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4646 strOldValue = it->second;
4647 }
4648
4649 bool fChanged;
4650 if ((fChanged = (strOldValue != strValue)))
4651 {
4652 // ask for permission from all listeners outside the locks;
4653 // onExtraDataCanChange() only briefly requests the VirtualBox
4654 // lock to copy the list of callbacks to invoke
4655 Bstr error;
4656 Bstr bstrValue(aValue);
4657
4658 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4659 {
4660 const char *sep = error.isEmpty() ? "" : ": ";
4661 CBSTR err = error.raw();
4662 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4663 sep, err));
4664 return setError(E_ACCESSDENIED,
4665 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4666 aKey,
4667 bstrValue.raw(),
4668 sep,
4669 err);
4670 }
4671
4672 // data is changing and change not vetoed: then write it out under the lock
4673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4674
4675 if (isSnapshotMachine())
4676 {
4677 HRESULT rc = checkStateDependency(MutableStateDep);
4678 if (FAILED(rc)) return rc;
4679 }
4680
4681 if (strValue.isEmpty())
4682 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4683 else
4684 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4685 // creates a new key if needed
4686
4687 bool fNeedsGlobalSaveSettings = false;
4688 saveSettings(&fNeedsGlobalSaveSettings);
4689
4690 if (fNeedsGlobalSaveSettings)
4691 {
4692 // save the global settings; for that we should hold only the VirtualBox lock
4693 alock.release();
4694 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4695 mParent->saveSettings();
4696 }
4697 }
4698
4699 // fire notification outside the lock
4700 if (fChanged)
4701 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4702
4703 return S_OK;
4704}
4705
4706STDMETHODIMP Machine::SaveSettings()
4707{
4708 AutoCaller autoCaller(this);
4709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4710
4711 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4712
4713 /* when there was auto-conversion, we want to save the file even if
4714 * the VM is saved */
4715 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4716 if (FAILED(rc)) return rc;
4717
4718 /* the settings file path may never be null */
4719 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4720
4721 /* save all VM data excluding snapshots */
4722 bool fNeedsGlobalSaveSettings = false;
4723 rc = saveSettings(&fNeedsGlobalSaveSettings);
4724 mlock.release();
4725
4726 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4727 {
4728 // save the global settings; for that we should hold only the VirtualBox lock
4729 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4730 rc = mParent->saveSettings();
4731 }
4732
4733 return rc;
4734}
4735
4736STDMETHODIMP Machine::DiscardSettings()
4737{
4738 AutoCaller autoCaller(this);
4739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4740
4741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 HRESULT rc = checkStateDependency(MutableStateDep);
4744 if (FAILED(rc)) return rc;
4745
4746 /*
4747 * during this rollback, the session will be notified if data has
4748 * been actually changed
4749 */
4750 rollback(true /* aNotify */);
4751
4752 return S_OK;
4753}
4754
4755/** @note Locks objects! */
4756STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4757 ComSafeArrayOut(IMedium*, aMedia))
4758{
4759 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4760 AutoLimitedCaller autoCaller(this);
4761 AssertComRCReturnRC(autoCaller.rc());
4762
4763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4764
4765 Guid id(getId());
4766
4767 if (mData->mSession.mState != SessionState_Unlocked)
4768 return setError(VBOX_E_INVALID_OBJECT_STATE,
4769 tr("Cannot unregister the machine '%s' while it is locked"),
4770 mUserData->s.strName.c_str());
4771
4772 // wait for state dependents to drop to zero
4773 ensureNoStateDependencies();
4774
4775 if (!mData->mAccessible)
4776 {
4777 // inaccessible maschines can only be unregistered; uninitialize ourselves
4778 // here because currently there may be no unregistered that are inaccessible
4779 // (this state combination is not supported). Note releasing the caller and
4780 // leaving the lock before calling uninit()
4781 alock.release();
4782 autoCaller.release();
4783
4784 uninit();
4785
4786 mParent->unregisterMachine(this, id);
4787 // calls VirtualBox::saveSettings()
4788
4789 return S_OK;
4790 }
4791
4792 HRESULT rc = S_OK;
4793
4794 // discard saved state
4795 if (mData->mMachineState == MachineState_Saved)
4796 {
4797 // add the saved state file to the list of files the caller should delete
4798 Assert(!mSSData->strStateFilePath.isEmpty());
4799 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4800
4801 mSSData->strStateFilePath.setNull();
4802
4803 // unconditionally set the machine state to powered off, we now
4804 // know no session has locked the machine
4805 mData->mMachineState = MachineState_PoweredOff;
4806 }
4807
4808 size_t cSnapshots = 0;
4809 if (mData->mFirstSnapshot)
4810 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4811 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4812 // fail now before we start detaching media
4813 return setError(VBOX_E_INVALID_OBJECT_STATE,
4814 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4815 mUserData->s.strName.c_str(), cSnapshots);
4816
4817 // This list collects the medium objects from all medium attachments
4818 // which we will detach from the machine and its snapshots, in a specific
4819 // order which allows for closing all media without getting "media in use"
4820 // errors, simply by going through the list from the front to the back:
4821 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4822 // and must be closed before the parent media from the snapshots, or closing the parents
4823 // will fail because they still have children);
4824 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4825 // the root ("first") snapshot of the machine.
4826 MediaList llMedia;
4827
4828 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4829 && mMediaData->mAttachments.size()
4830 )
4831 {
4832 // we have media attachments: detach them all and add the Medium objects to our list
4833 if (cleanupMode != CleanupMode_UnregisterOnly)
4834 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4835 else
4836 return setError(VBOX_E_INVALID_OBJECT_STATE,
4837 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4838 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4839 }
4840
4841 if (cSnapshots)
4842 {
4843 // autoCleanup must be true here, or we would have failed above
4844
4845 // add the media from the medium attachments of the snapshots to llMedia
4846 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4847 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4848 // into the children first
4849
4850 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4851 MachineState_T oldState = mData->mMachineState;
4852 mData->mMachineState = MachineState_DeletingSnapshot;
4853
4854 // make a copy of the first snapshot so the refcount does not drop to 0
4855 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4856 // because of the AutoCaller voodoo)
4857 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4858
4859 // GO!
4860 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4861
4862 mData->mMachineState = oldState;
4863 }
4864
4865 if (FAILED(rc))
4866 {
4867 rollbackMedia();
4868 return rc;
4869 }
4870
4871 // commit all the media changes made above
4872 commitMedia();
4873
4874 mData->mRegistered = false;
4875
4876 // machine lock no longer needed
4877 alock.release();
4878
4879 // return media to caller
4880 SafeIfaceArray<IMedium> sfaMedia(llMedia);
4881 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4882
4883 mParent->unregisterMachine(this, id);
4884 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
4885
4886 return S_OK;
4887}
4888
4889struct Machine::DeleteTask
4890{
4891 ComObjPtr<Machine> pMachine;
4892 RTCList<ComPtr<IMedium> > llMediums;
4893 StringsList llFilesToDelete;
4894 ComObjPtr<Progress> pProgress;
4895};
4896
4897STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4898{
4899 LogFlowFuncEnter();
4900
4901 AutoCaller autoCaller(this);
4902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4903
4904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4905
4906 HRESULT rc = checkStateDependency(MutableStateDep);
4907 if (FAILED(rc)) return rc;
4908
4909 if (mData->mRegistered)
4910 return setError(VBOX_E_INVALID_VM_STATE,
4911 tr("Cannot delete settings of a registered machine"));
4912
4913 DeleteTask *pTask = new DeleteTask;
4914 pTask->pMachine = this;
4915 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4916
4917 // collect files to delete
4918 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4919
4920 for (size_t i = 0; i < sfaMedia.size(); ++i)
4921 {
4922 IMedium *pIMedium(sfaMedia[i]);
4923 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4924 if (pMedium.isNull())
4925 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
4926 SafeArray<BSTR> ids;
4927 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4928 if (FAILED(rc)) return rc;
4929 /* At this point the medium should not have any back references
4930 * anymore. If it has it is attached to another VM and *must* not
4931 * deleted. */
4932 if (ids.size() < 1)
4933 pTask->llMediums.append(pMedium);
4934 }
4935 if (mData->pMachineConfigFile->fileExists())
4936 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4937
4938 pTask->pProgress.createObject();
4939 pTask->pProgress->init(getVirtualBox(),
4940 static_cast<IMachine*>(this) /* aInitiator */,
4941 Bstr(tr("Deleting files")).raw(),
4942 true /* fCancellable */,
4943 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
4944 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4945
4946 int vrc = RTThreadCreate(NULL,
4947 Machine::deleteThread,
4948 (void*)pTask,
4949 0,
4950 RTTHREADTYPE_MAIN_WORKER,
4951 0,
4952 "MachineDelete");
4953
4954 pTask->pProgress.queryInterfaceTo(aProgress);
4955
4956 if (RT_FAILURE(vrc))
4957 {
4958 delete pTask;
4959 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4960 }
4961
4962 LogFlowFuncLeave();
4963
4964 return S_OK;
4965}
4966
4967/**
4968 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4969 * calls Machine::deleteTaskWorker() on the actual machine object.
4970 * @param Thread
4971 * @param pvUser
4972 * @return
4973 */
4974/*static*/
4975DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
4976{
4977 LogFlowFuncEnter();
4978
4979 DeleteTask *pTask = (DeleteTask*)pvUser;
4980 Assert(pTask);
4981 Assert(pTask->pMachine);
4982 Assert(pTask->pProgress);
4983
4984 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
4985 pTask->pProgress->notifyComplete(rc);
4986
4987 delete pTask;
4988
4989 LogFlowFuncLeave();
4990
4991 NOREF(Thread);
4992
4993 return VINF_SUCCESS;
4994}
4995
4996/**
4997 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
4998 * @param task
4999 * @return
5000 */
5001HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5002{
5003 AutoCaller autoCaller(this);
5004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5005
5006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5007
5008 HRESULT rc = S_OK;
5009
5010 try
5011 {
5012 ULONG uLogHistoryCount = 3;
5013 ComPtr<ISystemProperties> systemProperties;
5014 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5015 if (FAILED(rc)) throw rc;
5016
5017 if (!systemProperties.isNull())
5018 {
5019 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5020 if (FAILED(rc)) throw rc;
5021 }
5022
5023 MachineState_T oldState = mData->mMachineState;
5024 setMachineState(MachineState_SettingUp);
5025 alock.release();
5026 for (size_t i = 0; i < task.llMediums.size(); ++i)
5027 {
5028 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5029 {
5030 AutoCaller mac(pMedium);
5031 if (FAILED(mac.rc())) throw mac.rc();
5032 Utf8Str strLocation = pMedium->getLocationFull();
5033 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5034 if (FAILED(rc)) throw rc;
5035 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5036 }
5037 ComPtr<IProgress> pProgress2;
5038 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5039 if (FAILED(rc)) throw rc;
5040 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5041 if (FAILED(rc)) throw rc;
5042 /* Check the result of the asynchrony process. */
5043 LONG iRc;
5044 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5045 if (FAILED(rc)) throw rc;
5046 /* If the thread of the progress object has an error, then
5047 * retrieve the error info from there, or it'll be lost. */
5048 if (FAILED(iRc))
5049 throw setError(ProgressErrorInfo(pProgress2));
5050 }
5051 setMachineState(oldState);
5052 alock.acquire();
5053
5054 // delete the files pushed on the task list by Machine::Delete()
5055 // (this includes saved states of the machine and snapshots and
5056 // medium storage files from the IMedium list passed in, and the
5057 // machine XML file)
5058 StringsList::const_iterator it = task.llFilesToDelete.begin();
5059 while (it != task.llFilesToDelete.end())
5060 {
5061 const Utf8Str &strFile = *it;
5062 LogFunc(("Deleting file %s\n", strFile.c_str()));
5063 int vrc = RTFileDelete(strFile.c_str());
5064 if (RT_FAILURE(vrc))
5065 throw setError(VBOX_E_IPRT_ERROR,
5066 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5067
5068 ++it;
5069 if (it == task.llFilesToDelete.end())
5070 {
5071 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5072 if (FAILED(rc)) throw rc;
5073 break;
5074 }
5075
5076 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5077 if (FAILED(rc)) throw rc;
5078 }
5079
5080 /* delete the settings only when the file actually exists */
5081 if (mData->pMachineConfigFile->fileExists())
5082 {
5083 /* Delete any backup or uncommitted XML files. Ignore failures.
5084 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5085 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5086 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5087 RTFileDelete(otherXml.c_str());
5088 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5089 RTFileDelete(otherXml.c_str());
5090
5091 /* delete the Logs folder, nothing important should be left
5092 * there (we don't check for errors because the user might have
5093 * some private files there that we don't want to delete) */
5094 Utf8Str logFolder;
5095 getLogFolder(logFolder);
5096 Assert(logFolder.length());
5097 if (RTDirExists(logFolder.c_str()))
5098 {
5099 /* Delete all VBox.log[.N] files from the Logs folder
5100 * (this must be in sync with the rotation logic in
5101 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5102 * files that may have been created by the GUI. */
5103 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5104 logFolder.c_str(), RTPATH_DELIMITER);
5105 RTFileDelete(log.c_str());
5106 log = Utf8StrFmt("%s%cVBox.png",
5107 logFolder.c_str(), RTPATH_DELIMITER);
5108 RTFileDelete(log.c_str());
5109 for (int i = uLogHistoryCount; i > 0; i--)
5110 {
5111 log = Utf8StrFmt("%s%cVBox.log.%d",
5112 logFolder.c_str(), RTPATH_DELIMITER, i);
5113 RTFileDelete(log.c_str());
5114 log = Utf8StrFmt("%s%cVBox.png.%d",
5115 logFolder.c_str(), RTPATH_DELIMITER, i);
5116 RTFileDelete(log.c_str());
5117 }
5118
5119 RTDirRemove(logFolder.c_str());
5120 }
5121
5122 /* delete the Snapshots folder, nothing important should be left
5123 * there (we don't check for errors because the user might have
5124 * some private files there that we don't want to delete) */
5125 Utf8Str strFullSnapshotFolder;
5126 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5127 Assert(!strFullSnapshotFolder.isEmpty());
5128 if (RTDirExists(strFullSnapshotFolder.c_str()))
5129 RTDirRemove(strFullSnapshotFolder.c_str());
5130
5131 // delete the directory that contains the settings file, but only
5132 // if it matches the VM name
5133 Utf8Str settingsDir;
5134 if (isInOwnDir(&settingsDir))
5135 RTDirRemove(settingsDir.c_str());
5136 }
5137
5138 alock.release();
5139
5140 mParent->saveModifiedRegistries();
5141 }
5142 catch (HRESULT aRC) { rc = aRC; }
5143
5144 return rc;
5145}
5146
5147STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5148{
5149 CheckComArgOutPointerValid(aSnapshot);
5150
5151 AutoCaller autoCaller(this);
5152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5153
5154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5155
5156 ComObjPtr<Snapshot> pSnapshot;
5157 HRESULT rc;
5158
5159 if (!aNameOrId || !*aNameOrId)
5160 // null case (caller wants root snapshot): findSnapshotById() handles this
5161 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5162 else
5163 {
5164 Guid uuid(aNameOrId);
5165 if (!uuid.isEmpty())
5166 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5167 else
5168 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5169 }
5170 pSnapshot.queryInterfaceTo(aSnapshot);
5171
5172 return rc;
5173}
5174
5175STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5176{
5177 CheckComArgStrNotEmptyOrNull(aName);
5178 CheckComArgStrNotEmptyOrNull(aHostPath);
5179
5180 AutoCaller autoCaller(this);
5181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5182
5183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5184
5185 HRESULT rc = checkStateDependency(MutableStateDep);
5186 if (FAILED(rc)) return rc;
5187
5188 Utf8Str strName(aName);
5189
5190 ComObjPtr<SharedFolder> sharedFolder;
5191 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5192 if (SUCCEEDED(rc))
5193 return setError(VBOX_E_OBJECT_IN_USE,
5194 tr("Shared folder named '%s' already exists"),
5195 strName.c_str());
5196
5197 sharedFolder.createObject();
5198 rc = sharedFolder->init(getMachine(),
5199 strName,
5200 aHostPath,
5201 !!aWritable,
5202 !!aAutoMount,
5203 true /* fFailOnError */);
5204 if (FAILED(rc)) return rc;
5205
5206 setModified(IsModified_SharedFolders);
5207 mHWData.backup();
5208 mHWData->mSharedFolders.push_back(sharedFolder);
5209
5210 /* inform the direct session if any */
5211 alock.release();
5212 onSharedFolderChange();
5213
5214 return S_OK;
5215}
5216
5217STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5218{
5219 CheckComArgStrNotEmptyOrNull(aName);
5220
5221 AutoCaller autoCaller(this);
5222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5223
5224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5225
5226 HRESULT rc = checkStateDependency(MutableStateDep);
5227 if (FAILED(rc)) return rc;
5228
5229 ComObjPtr<SharedFolder> sharedFolder;
5230 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5231 if (FAILED(rc)) return rc;
5232
5233 setModified(IsModified_SharedFolders);
5234 mHWData.backup();
5235 mHWData->mSharedFolders.remove(sharedFolder);
5236
5237 /* inform the direct session if any */
5238 alock.release();
5239 onSharedFolderChange();
5240
5241 return S_OK;
5242}
5243
5244STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5245{
5246 CheckComArgOutPointerValid(aCanShow);
5247
5248 /* start with No */
5249 *aCanShow = FALSE;
5250
5251 AutoCaller autoCaller(this);
5252 AssertComRCReturnRC(autoCaller.rc());
5253
5254 ComPtr<IInternalSessionControl> directControl;
5255 {
5256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5257
5258 if (mData->mSession.mState != SessionState_Locked)
5259 return setError(VBOX_E_INVALID_VM_STATE,
5260 tr("Machine is not locked for session (session state: %s)"),
5261 Global::stringifySessionState(mData->mSession.mState));
5262
5263 directControl = mData->mSession.mDirectControl;
5264 }
5265
5266 /* ignore calls made after #OnSessionEnd() is called */
5267 if (!directControl)
5268 return S_OK;
5269
5270 LONG64 dummy;
5271 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5272}
5273
5274STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5275{
5276 CheckComArgOutPointerValid(aWinId);
5277
5278 AutoCaller autoCaller(this);
5279 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5280
5281 ComPtr<IInternalSessionControl> directControl;
5282 {
5283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5284
5285 if (mData->mSession.mState != SessionState_Locked)
5286 return setError(E_FAIL,
5287 tr("Machine is not locked for session (session state: %s)"),
5288 Global::stringifySessionState(mData->mSession.mState));
5289
5290 directControl = mData->mSession.mDirectControl;
5291 }
5292
5293 /* ignore calls made after #OnSessionEnd() is called */
5294 if (!directControl)
5295 return S_OK;
5296
5297 BOOL dummy;
5298 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5299}
5300
5301#ifdef VBOX_WITH_GUEST_PROPS
5302/**
5303 * Look up a guest property in VBoxSVC's internal structures.
5304 */
5305HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5306 BSTR *aValue,
5307 LONG64 *aTimestamp,
5308 BSTR *aFlags) const
5309{
5310 using namespace guestProp;
5311
5312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5313 Utf8Str strName(aName);
5314 HWData::GuestPropertyList::const_iterator it;
5315
5316 for (it = mHWData->mGuestProperties.begin();
5317 it != mHWData->mGuestProperties.end(); ++it)
5318 {
5319 if (it->strName == strName)
5320 {
5321 char szFlags[MAX_FLAGS_LEN + 1];
5322 it->strValue.cloneTo(aValue);
5323 *aTimestamp = it->mTimestamp;
5324 writeFlags(it->mFlags, szFlags);
5325 Bstr(szFlags).cloneTo(aFlags);
5326 break;
5327 }
5328 }
5329 return S_OK;
5330}
5331
5332/**
5333 * Query the VM that a guest property belongs to for the property.
5334 * @returns E_ACCESSDENIED if the VM process is not available or not
5335 * currently handling queries and the lookup should then be done in
5336 * VBoxSVC.
5337 */
5338HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5339 BSTR *aValue,
5340 LONG64 *aTimestamp,
5341 BSTR *aFlags) const
5342{
5343 HRESULT rc;
5344 ComPtr<IInternalSessionControl> directControl;
5345 directControl = mData->mSession.mDirectControl;
5346
5347 /* fail if we were called after #OnSessionEnd() is called. This is a
5348 * silly race condition. */
5349
5350 if (!directControl)
5351 rc = E_ACCESSDENIED;
5352 else
5353 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5354 false /* isSetter */,
5355 aValue, aTimestamp, aFlags);
5356 return rc;
5357}
5358#endif // VBOX_WITH_GUEST_PROPS
5359
5360STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5361 BSTR *aValue,
5362 LONG64 *aTimestamp,
5363 BSTR *aFlags)
5364{
5365#ifndef VBOX_WITH_GUEST_PROPS
5366 ReturnComNotImplemented();
5367#else // VBOX_WITH_GUEST_PROPS
5368 CheckComArgStrNotEmptyOrNull(aName);
5369 CheckComArgOutPointerValid(aValue);
5370 CheckComArgOutPointerValid(aTimestamp);
5371 CheckComArgOutPointerValid(aFlags);
5372
5373 AutoCaller autoCaller(this);
5374 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5375
5376 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5377 if (rc == E_ACCESSDENIED)
5378 /* The VM is not running or the service is not (yet) accessible */
5379 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5380 return rc;
5381#endif // VBOX_WITH_GUEST_PROPS
5382}
5383
5384STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5385{
5386 LONG64 dummyTimestamp;
5387 Bstr dummyFlags;
5388 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5389}
5390
5391STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5392{
5393 Bstr dummyValue;
5394 Bstr dummyFlags;
5395 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5396}
5397
5398#ifdef VBOX_WITH_GUEST_PROPS
5399/**
5400 * Set a guest property in VBoxSVC's internal structures.
5401 */
5402HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5403 IN_BSTR aFlags)
5404{
5405 using namespace guestProp;
5406
5407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5408 HRESULT rc = S_OK;
5409 HWData::GuestProperty property;
5410 property.mFlags = NILFLAG;
5411 bool found = false;
5412
5413 rc = checkStateDependency(MutableStateDep);
5414 if (FAILED(rc)) return rc;
5415
5416 try
5417 {
5418 Utf8Str utf8Name(aName);
5419 Utf8Str utf8Flags(aFlags);
5420 uint32_t fFlags = NILFLAG;
5421 if ( (aFlags != NULL)
5422 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5423 )
5424 return setError(E_INVALIDARG,
5425 tr("Invalid flag values: '%ls'"),
5426 aFlags);
5427
5428 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5429 * know, this is simple and do an OK job atm.) */
5430 HWData::GuestPropertyList::iterator it;
5431 for (it = mHWData->mGuestProperties.begin();
5432 it != mHWData->mGuestProperties.end(); ++it)
5433 if (it->strName == utf8Name)
5434 {
5435 property = *it;
5436 if (it->mFlags & (RDONLYHOST))
5437 rc = setError(E_ACCESSDENIED,
5438 tr("The property '%ls' cannot be changed by the host"),
5439 aName);
5440 else
5441 {
5442 setModified(IsModified_MachineData);
5443 mHWData.backup(); // @todo r=dj backup in a loop?!?
5444
5445 /* The backup() operation invalidates our iterator, so
5446 * get a new one. */
5447 for (it = mHWData->mGuestProperties.begin();
5448 it->strName != utf8Name;
5449 ++it)
5450 ;
5451 mHWData->mGuestProperties.erase(it);
5452 }
5453 found = true;
5454 break;
5455 }
5456 if (found && SUCCEEDED(rc))
5457 {
5458 if (aValue)
5459 {
5460 RTTIMESPEC time;
5461 property.strValue = aValue;
5462 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5463 if (aFlags != NULL)
5464 property.mFlags = fFlags;
5465 mHWData->mGuestProperties.push_back(property);
5466 }
5467 }
5468 else if (SUCCEEDED(rc) && aValue)
5469 {
5470 RTTIMESPEC time;
5471 setModified(IsModified_MachineData);
5472 mHWData.backup();
5473 property.strName = aName;
5474 property.strValue = aValue;
5475 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5476 property.mFlags = fFlags;
5477 mHWData->mGuestProperties.push_back(property);
5478 }
5479 if ( SUCCEEDED(rc)
5480 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5481 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5482 RTSTR_MAX,
5483 utf8Name.c_str(),
5484 RTSTR_MAX,
5485 NULL)
5486 )
5487 )
5488 {
5489 /** @todo r=bird: Why aren't we leaving the lock here? The
5490 * same code in PushGuestProperty does... */
5491 mParent->onGuestPropertyChange(mData->mUuid, aName,
5492 aValue ? aValue : Bstr("").raw(),
5493 aFlags ? aFlags : Bstr("").raw());
5494 }
5495 }
5496 catch (std::bad_alloc &)
5497 {
5498 rc = E_OUTOFMEMORY;
5499 }
5500
5501 return rc;
5502}
5503
5504/**
5505 * Set a property on the VM that that property belongs to.
5506 * @returns E_ACCESSDENIED if the VM process is not available or not
5507 * currently handling queries and the setting should then be done in
5508 * VBoxSVC.
5509 */
5510HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5511 IN_BSTR aFlags)
5512{
5513 HRESULT rc;
5514
5515 try
5516 {
5517 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5518
5519 BSTR dummy = NULL; /* will not be changed (setter) */
5520 LONG64 dummy64;
5521 if (!directControl)
5522 rc = E_ACCESSDENIED;
5523 else
5524 /** @todo Fix when adding DeleteGuestProperty(),
5525 see defect. */
5526 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5527 true /* isSetter */,
5528 &dummy, &dummy64, &dummy);
5529 }
5530 catch (std::bad_alloc &)
5531 {
5532 rc = E_OUTOFMEMORY;
5533 }
5534
5535 return rc;
5536}
5537#endif // VBOX_WITH_GUEST_PROPS
5538
5539STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5540 IN_BSTR aFlags)
5541{
5542#ifndef VBOX_WITH_GUEST_PROPS
5543 ReturnComNotImplemented();
5544#else // VBOX_WITH_GUEST_PROPS
5545 CheckComArgStrNotEmptyOrNull(aName);
5546 CheckComArgMaybeNull(aFlags);
5547 CheckComArgMaybeNull(aValue);
5548
5549 AutoCaller autoCaller(this);
5550 if (FAILED(autoCaller.rc()))
5551 return autoCaller.rc();
5552
5553 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5554 if (rc == E_ACCESSDENIED)
5555 /* The VM is not running or the service is not (yet) accessible */
5556 rc = setGuestPropertyToService(aName, aValue, aFlags);
5557 return rc;
5558#endif // VBOX_WITH_GUEST_PROPS
5559}
5560
5561STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5562{
5563 return SetGuestProperty(aName, aValue, NULL);
5564}
5565
5566STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5567{
5568 return SetGuestProperty(aName, NULL, NULL);
5569}
5570
5571#ifdef VBOX_WITH_GUEST_PROPS
5572/**
5573 * Enumerate the guest properties in VBoxSVC's internal structures.
5574 */
5575HRESULT Machine::enumerateGuestPropertiesInService
5576 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5577 ComSafeArrayOut(BSTR, aValues),
5578 ComSafeArrayOut(LONG64, aTimestamps),
5579 ComSafeArrayOut(BSTR, aFlags))
5580{
5581 using namespace guestProp;
5582
5583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5584 Utf8Str strPatterns(aPatterns);
5585
5586 /*
5587 * Look for matching patterns and build up a list.
5588 */
5589 HWData::GuestPropertyList propList;
5590 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5591 it != mHWData->mGuestProperties.end();
5592 ++it)
5593 if ( strPatterns.isEmpty()
5594 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5595 RTSTR_MAX,
5596 it->strName.c_str(),
5597 RTSTR_MAX,
5598 NULL)
5599 )
5600 propList.push_back(*it);
5601
5602 /*
5603 * And build up the arrays for returning the property information.
5604 */
5605 size_t cEntries = propList.size();
5606 SafeArray<BSTR> names(cEntries);
5607 SafeArray<BSTR> values(cEntries);
5608 SafeArray<LONG64> timestamps(cEntries);
5609 SafeArray<BSTR> flags(cEntries);
5610 size_t iProp = 0;
5611 for (HWData::GuestPropertyList::iterator it = propList.begin();
5612 it != propList.end();
5613 ++it)
5614 {
5615 char szFlags[MAX_FLAGS_LEN + 1];
5616 it->strName.cloneTo(&names[iProp]);
5617 it->strValue.cloneTo(&values[iProp]);
5618 timestamps[iProp] = it->mTimestamp;
5619 writeFlags(it->mFlags, szFlags);
5620 Bstr(szFlags).cloneTo(&flags[iProp]);
5621 ++iProp;
5622 }
5623 names.detachTo(ComSafeArrayOutArg(aNames));
5624 values.detachTo(ComSafeArrayOutArg(aValues));
5625 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5626 flags.detachTo(ComSafeArrayOutArg(aFlags));
5627 return S_OK;
5628}
5629
5630/**
5631 * Enumerate the properties managed by a VM.
5632 * @returns E_ACCESSDENIED if the VM process is not available or not
5633 * currently handling queries and the setting should then be done in
5634 * VBoxSVC.
5635 */
5636HRESULT Machine::enumerateGuestPropertiesOnVM
5637 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5638 ComSafeArrayOut(BSTR, aValues),
5639 ComSafeArrayOut(LONG64, aTimestamps),
5640 ComSafeArrayOut(BSTR, aFlags))
5641{
5642 HRESULT rc;
5643 ComPtr<IInternalSessionControl> directControl;
5644 directControl = mData->mSession.mDirectControl;
5645
5646 if (!directControl)
5647 rc = E_ACCESSDENIED;
5648 else
5649 rc = directControl->EnumerateGuestProperties
5650 (aPatterns, ComSafeArrayOutArg(aNames),
5651 ComSafeArrayOutArg(aValues),
5652 ComSafeArrayOutArg(aTimestamps),
5653 ComSafeArrayOutArg(aFlags));
5654 return rc;
5655}
5656#endif // VBOX_WITH_GUEST_PROPS
5657
5658STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5659 ComSafeArrayOut(BSTR, aNames),
5660 ComSafeArrayOut(BSTR, aValues),
5661 ComSafeArrayOut(LONG64, aTimestamps),
5662 ComSafeArrayOut(BSTR, aFlags))
5663{
5664#ifndef VBOX_WITH_GUEST_PROPS
5665 ReturnComNotImplemented();
5666#else // VBOX_WITH_GUEST_PROPS
5667 CheckComArgMaybeNull(aPatterns);
5668 CheckComArgOutSafeArrayPointerValid(aNames);
5669 CheckComArgOutSafeArrayPointerValid(aValues);
5670 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5671 CheckComArgOutSafeArrayPointerValid(aFlags);
5672
5673 AutoCaller autoCaller(this);
5674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5675
5676 HRESULT rc = enumerateGuestPropertiesOnVM
5677 (aPatterns, ComSafeArrayOutArg(aNames),
5678 ComSafeArrayOutArg(aValues),
5679 ComSafeArrayOutArg(aTimestamps),
5680 ComSafeArrayOutArg(aFlags));
5681 if (rc == E_ACCESSDENIED)
5682 /* The VM is not running or the service is not (yet) accessible */
5683 rc = enumerateGuestPropertiesInService
5684 (aPatterns, ComSafeArrayOutArg(aNames),
5685 ComSafeArrayOutArg(aValues),
5686 ComSafeArrayOutArg(aTimestamps),
5687 ComSafeArrayOutArg(aFlags));
5688 return rc;
5689#endif // VBOX_WITH_GUEST_PROPS
5690}
5691
5692STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5693 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5694{
5695 MediaData::AttachmentList atts;
5696
5697 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5698 if (FAILED(rc)) return rc;
5699
5700 SafeIfaceArray<IMediumAttachment> attachments(atts);
5701 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5702
5703 return S_OK;
5704}
5705
5706STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5707 LONG aControllerPort,
5708 LONG aDevice,
5709 IMediumAttachment **aAttachment)
5710{
5711 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5712 aControllerName, aControllerPort, aDevice));
5713
5714 CheckComArgStrNotEmptyOrNull(aControllerName);
5715 CheckComArgOutPointerValid(aAttachment);
5716
5717 AutoCaller autoCaller(this);
5718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5719
5720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5721
5722 *aAttachment = NULL;
5723
5724 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5725 aControllerName,
5726 aControllerPort,
5727 aDevice);
5728 if (pAttach.isNull())
5729 return setError(VBOX_E_OBJECT_NOT_FOUND,
5730 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5731 aDevice, aControllerPort, aControllerName);
5732
5733 pAttach.queryInterfaceTo(aAttachment);
5734
5735 return S_OK;
5736}
5737
5738STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5739 StorageBus_T aConnectionType,
5740 IStorageController **controller)
5741{
5742 CheckComArgStrNotEmptyOrNull(aName);
5743
5744 if ( (aConnectionType <= StorageBus_Null)
5745 || (aConnectionType > StorageBus_SAS))
5746 return setError(E_INVALIDARG,
5747 tr("Invalid connection type: %d"),
5748 aConnectionType);
5749
5750 AutoCaller autoCaller(this);
5751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5752
5753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5754
5755 HRESULT rc = checkStateDependency(MutableStateDep);
5756 if (FAILED(rc)) return rc;
5757
5758 /* try to find one with the name first. */
5759 ComObjPtr<StorageController> ctrl;
5760
5761 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5762 if (SUCCEEDED(rc))
5763 return setError(VBOX_E_OBJECT_IN_USE,
5764 tr("Storage controller named '%ls' already exists"),
5765 aName);
5766
5767 ctrl.createObject();
5768
5769 /* get a new instance number for the storage controller */
5770 ULONG ulInstance = 0;
5771 bool fBootable = true;
5772 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5773 it != mStorageControllers->end();
5774 ++it)
5775 {
5776 if ((*it)->getStorageBus() == aConnectionType)
5777 {
5778 ULONG ulCurInst = (*it)->getInstance();
5779
5780 if (ulCurInst >= ulInstance)
5781 ulInstance = ulCurInst + 1;
5782
5783 /* Only one controller of each type can be marked as bootable. */
5784 if ((*it)->getBootable())
5785 fBootable = false;
5786 }
5787 }
5788
5789 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5790 if (FAILED(rc)) return rc;
5791
5792 setModified(IsModified_Storage);
5793 mStorageControllers.backup();
5794 mStorageControllers->push_back(ctrl);
5795
5796 ctrl.queryInterfaceTo(controller);
5797
5798 /* inform the direct session if any */
5799 alock.release();
5800 onStorageControllerChange();
5801
5802 return S_OK;
5803}
5804
5805STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5806 IStorageController **aStorageController)
5807{
5808 CheckComArgStrNotEmptyOrNull(aName);
5809
5810 AutoCaller autoCaller(this);
5811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5812
5813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5814
5815 ComObjPtr<StorageController> ctrl;
5816
5817 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5818 if (SUCCEEDED(rc))
5819 ctrl.queryInterfaceTo(aStorageController);
5820
5821 return rc;
5822}
5823
5824STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5825 IStorageController **aStorageController)
5826{
5827 AutoCaller autoCaller(this);
5828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5829
5830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5831
5832 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5833 it != mStorageControllers->end();
5834 ++it)
5835 {
5836 if ((*it)->getInstance() == aInstance)
5837 {
5838 (*it).queryInterfaceTo(aStorageController);
5839 return S_OK;
5840 }
5841 }
5842
5843 return setError(VBOX_E_OBJECT_NOT_FOUND,
5844 tr("Could not find a storage controller with instance number '%lu'"),
5845 aInstance);
5846}
5847
5848STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5849{
5850 AutoCaller autoCaller(this);
5851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5852
5853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5854
5855 HRESULT rc = checkStateDependency(MutableStateDep);
5856 if (FAILED(rc)) return rc;
5857
5858 ComObjPtr<StorageController> ctrl;
5859
5860 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5861 if (SUCCEEDED(rc))
5862 {
5863 /* Ensure that only one controller of each type is marked as bootable. */
5864 if (fBootable == TRUE)
5865 {
5866 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5867 it != mStorageControllers->end();
5868 ++it)
5869 {
5870 ComObjPtr<StorageController> aCtrl = (*it);
5871
5872 if ( (aCtrl->getName() != Utf8Str(aName))
5873 && aCtrl->getBootable() == TRUE
5874 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5875 && aCtrl->getControllerType() == ctrl->getControllerType())
5876 {
5877 aCtrl->setBootable(FALSE);
5878 break;
5879 }
5880 }
5881 }
5882
5883 if (SUCCEEDED(rc))
5884 {
5885 ctrl->setBootable(fBootable);
5886 setModified(IsModified_Storage);
5887 }
5888 }
5889
5890 if (SUCCEEDED(rc))
5891 {
5892 /* inform the direct session if any */
5893 alock.release();
5894 onStorageControllerChange();
5895 }
5896
5897 return rc;
5898}
5899
5900STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5901{
5902 CheckComArgStrNotEmptyOrNull(aName);
5903
5904 AutoCaller autoCaller(this);
5905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5906
5907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5908
5909 HRESULT rc = checkStateDependency(MutableStateDep);
5910 if (FAILED(rc)) return rc;
5911
5912 ComObjPtr<StorageController> ctrl;
5913 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5914 if (FAILED(rc)) return rc;
5915
5916 {
5917 /* find all attached devices to the appropriate storage controller and detach them all*/
5918 MediaData::AttachmentList::const_iterator endList = mMediaData->mAttachments.end();
5919 MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5920 for (;it != endList; it++)
5921 {
5922 MediumAttachment *pAttachTemp = *it;
5923 AutoCaller localAutoCaller(pAttachTemp);
5924 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5925
5926 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5927
5928 if (pAttachTemp->getControllerName() == aName)
5929 {
5930 LONG port = pAttachTemp->getPort();
5931 LONG device = pAttachTemp->getDevice();
5932 rc = DetachDevice(aName, port, device);
5933 if (FAILED(rc)) return rc;
5934 }
5935 }
5936 }
5937
5938 /* We can remove it now. */
5939 setModified(IsModified_Storage);
5940 mStorageControllers.backup();
5941
5942 ctrl->unshare();
5943
5944 mStorageControllers->remove(ctrl);
5945
5946 /* inform the direct session if any */
5947 alock.release();
5948 onStorageControllerChange();
5949
5950 return S_OK;
5951}
5952
5953STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5954 ULONG *puOriginX,
5955 ULONG *puOriginY,
5956 ULONG *puWidth,
5957 ULONG *puHeight,
5958 BOOL *pfEnabled)
5959{
5960 LogFlowThisFunc(("\n"));
5961
5962 CheckComArgNotNull(puOriginX);
5963 CheckComArgNotNull(puOriginY);
5964 CheckComArgNotNull(puWidth);
5965 CheckComArgNotNull(puHeight);
5966 CheckComArgNotNull(pfEnabled);
5967
5968 uint32_t u32OriginX= 0;
5969 uint32_t u32OriginY= 0;
5970 uint32_t u32Width = 0;
5971 uint32_t u32Height = 0;
5972 uint16_t u16Flags = 0;
5973
5974 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
5975 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5976 if (RT_FAILURE(vrc))
5977 {
5978#ifdef RT_OS_WINDOWS
5979 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5980 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5981 * So just assign fEnable to TRUE again.
5982 * The right fix would be to change GUI API wrappers to make sure that parameters
5983 * are changed only if API succeeds.
5984 */
5985 *pfEnabled = TRUE;
5986#endif
5987 return setError(VBOX_E_IPRT_ERROR,
5988 tr("Saved guest size is not available (%Rrc)"),
5989 vrc);
5990 }
5991
5992 *puOriginX = u32OriginX;
5993 *puOriginY = u32OriginY;
5994 *puWidth = u32Width;
5995 *puHeight = u32Height;
5996 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5997
5998 return S_OK;
5999}
6000
6001STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6002{
6003 LogFlowThisFunc(("\n"));
6004
6005 CheckComArgNotNull(aSize);
6006 CheckComArgNotNull(aWidth);
6007 CheckComArgNotNull(aHeight);
6008
6009 if (aScreenId != 0)
6010 return E_NOTIMPL;
6011
6012 AutoCaller autoCaller(this);
6013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6014
6015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6016
6017 uint8_t *pu8Data = NULL;
6018 uint32_t cbData = 0;
6019 uint32_t u32Width = 0;
6020 uint32_t u32Height = 0;
6021
6022 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6023
6024 if (RT_FAILURE(vrc))
6025 return setError(VBOX_E_IPRT_ERROR,
6026 tr("Saved screenshot data is not available (%Rrc)"),
6027 vrc);
6028
6029 *aSize = cbData;
6030 *aWidth = u32Width;
6031 *aHeight = u32Height;
6032
6033 freeSavedDisplayScreenshot(pu8Data);
6034
6035 return S_OK;
6036}
6037
6038STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6039{
6040 LogFlowThisFunc(("\n"));
6041
6042 CheckComArgNotNull(aWidth);
6043 CheckComArgNotNull(aHeight);
6044 CheckComArgOutSafeArrayPointerValid(aData);
6045
6046 if (aScreenId != 0)
6047 return E_NOTIMPL;
6048
6049 AutoCaller autoCaller(this);
6050 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6051
6052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6053
6054 uint8_t *pu8Data = NULL;
6055 uint32_t cbData = 0;
6056 uint32_t u32Width = 0;
6057 uint32_t u32Height = 0;
6058
6059 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6060
6061 if (RT_FAILURE(vrc))
6062 return setError(VBOX_E_IPRT_ERROR,
6063 tr("Saved screenshot data is not available (%Rrc)"),
6064 vrc);
6065
6066 *aWidth = u32Width;
6067 *aHeight = u32Height;
6068
6069 com::SafeArray<BYTE> bitmap(cbData);
6070 /* Convert pixels to format expected by the API caller. */
6071 if (aBGR)
6072 {
6073 /* [0] B, [1] G, [2] R, [3] A. */
6074 for (unsigned i = 0; i < cbData; i += 4)
6075 {
6076 bitmap[i] = pu8Data[i];
6077 bitmap[i + 1] = pu8Data[i + 1];
6078 bitmap[i + 2] = pu8Data[i + 2];
6079 bitmap[i + 3] = 0xff;
6080 }
6081 }
6082 else
6083 {
6084 /* [0] R, [1] G, [2] B, [3] A. */
6085 for (unsigned i = 0; i < cbData; i += 4)
6086 {
6087 bitmap[i] = pu8Data[i + 2];
6088 bitmap[i + 1] = pu8Data[i + 1];
6089 bitmap[i + 2] = pu8Data[i];
6090 bitmap[i + 3] = 0xff;
6091 }
6092 }
6093 bitmap.detachTo(ComSafeArrayOutArg(aData));
6094
6095 freeSavedDisplayScreenshot(pu8Data);
6096
6097 return S_OK;
6098}
6099
6100
6101STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6102{
6103 LogFlowThisFunc(("\n"));
6104
6105 CheckComArgNotNull(aWidth);
6106 CheckComArgNotNull(aHeight);
6107 CheckComArgOutSafeArrayPointerValid(aData);
6108
6109 if (aScreenId != 0)
6110 return E_NOTIMPL;
6111
6112 AutoCaller autoCaller(this);
6113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6114
6115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6116
6117 uint8_t *pu8Data = NULL;
6118 uint32_t cbData = 0;
6119 uint32_t u32Width = 0;
6120 uint32_t u32Height = 0;
6121
6122 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6123
6124 if (RT_FAILURE(vrc))
6125 return setError(VBOX_E_IPRT_ERROR,
6126 tr("Saved screenshot data is not available (%Rrc)"),
6127 vrc);
6128
6129 *aWidth = u32Width;
6130 *aHeight = u32Height;
6131
6132 HRESULT rc = S_OK;
6133 uint8_t *pu8PNG = NULL;
6134 uint32_t cbPNG = 0;
6135 uint32_t cxPNG = 0;
6136 uint32_t cyPNG = 0;
6137
6138 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6139
6140 if (RT_SUCCESS(vrc))
6141 {
6142 com::SafeArray<BYTE> screenData(cbPNG);
6143 screenData.initFrom(pu8PNG, cbPNG);
6144 if (pu8PNG)
6145 RTMemFree(pu8PNG);
6146 screenData.detachTo(ComSafeArrayOutArg(aData));
6147 }
6148 else
6149 {
6150 if (pu8PNG)
6151 RTMemFree(pu8PNG);
6152 return setError(VBOX_E_IPRT_ERROR,
6153 tr("Could not convert screenshot to PNG (%Rrc)"),
6154 vrc);
6155 }
6156
6157 freeSavedDisplayScreenshot(pu8Data);
6158
6159 return rc;
6160}
6161
6162STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6163{
6164 LogFlowThisFunc(("\n"));
6165
6166 CheckComArgNotNull(aSize);
6167 CheckComArgNotNull(aWidth);
6168 CheckComArgNotNull(aHeight);
6169
6170 if (aScreenId != 0)
6171 return E_NOTIMPL;
6172
6173 AutoCaller autoCaller(this);
6174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6175
6176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6177
6178 uint8_t *pu8Data = NULL;
6179 uint32_t cbData = 0;
6180 uint32_t u32Width = 0;
6181 uint32_t u32Height = 0;
6182
6183 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6184
6185 if (RT_FAILURE(vrc))
6186 return setError(VBOX_E_IPRT_ERROR,
6187 tr("Saved screenshot data is not available (%Rrc)"),
6188 vrc);
6189
6190 *aSize = cbData;
6191 *aWidth = u32Width;
6192 *aHeight = u32Height;
6193
6194 freeSavedDisplayScreenshot(pu8Data);
6195
6196 return S_OK;
6197}
6198
6199STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6200{
6201 LogFlowThisFunc(("\n"));
6202
6203 CheckComArgNotNull(aWidth);
6204 CheckComArgNotNull(aHeight);
6205 CheckComArgOutSafeArrayPointerValid(aData);
6206
6207 if (aScreenId != 0)
6208 return E_NOTIMPL;
6209
6210 AutoCaller autoCaller(this);
6211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6212
6213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6214
6215 uint8_t *pu8Data = NULL;
6216 uint32_t cbData = 0;
6217 uint32_t u32Width = 0;
6218 uint32_t u32Height = 0;
6219
6220 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6221
6222 if (RT_FAILURE(vrc))
6223 return setError(VBOX_E_IPRT_ERROR,
6224 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6225 vrc);
6226
6227 *aWidth = u32Width;
6228 *aHeight = u32Height;
6229
6230 com::SafeArray<BYTE> png(cbData);
6231 png.initFrom(pu8Data, cbData);
6232 png.detachTo(ComSafeArrayOutArg(aData));
6233
6234 freeSavedDisplayScreenshot(pu8Data);
6235
6236 return S_OK;
6237}
6238
6239STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6240{
6241 HRESULT rc = S_OK;
6242 LogFlowThisFunc(("\n"));
6243
6244 AutoCaller autoCaller(this);
6245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6246
6247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6248
6249 if (!mHWData->mCPUHotPlugEnabled)
6250 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6251
6252 if (aCpu >= mHWData->mCPUCount)
6253 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6254
6255 if (mHWData->mCPUAttached[aCpu])
6256 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6257
6258 alock.release();
6259 rc = onCPUChange(aCpu, false);
6260 alock.acquire();
6261 if (FAILED(rc)) return rc;
6262
6263 setModified(IsModified_MachineData);
6264 mHWData.backup();
6265 mHWData->mCPUAttached[aCpu] = true;
6266
6267 /* Save settings if online */
6268 if (Global::IsOnline(mData->mMachineState))
6269 saveSettings(NULL);
6270
6271 return S_OK;
6272}
6273
6274STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6275{
6276 HRESULT rc = S_OK;
6277 LogFlowThisFunc(("\n"));
6278
6279 AutoCaller autoCaller(this);
6280 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6281
6282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6283
6284 if (!mHWData->mCPUHotPlugEnabled)
6285 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6286
6287 if (aCpu >= SchemaDefs::MaxCPUCount)
6288 return setError(E_INVALIDARG,
6289 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6290 SchemaDefs::MaxCPUCount);
6291
6292 if (!mHWData->mCPUAttached[aCpu])
6293 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6294
6295 /* CPU 0 can't be detached */
6296 if (aCpu == 0)
6297 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6298
6299 alock.release();
6300 rc = onCPUChange(aCpu, true);
6301 alock.acquire();
6302 if (FAILED(rc)) return rc;
6303
6304 setModified(IsModified_MachineData);
6305 mHWData.backup();
6306 mHWData->mCPUAttached[aCpu] = false;
6307
6308 /* Save settings if online */
6309 if (Global::IsOnline(mData->mMachineState))
6310 saveSettings(NULL);
6311
6312 return S_OK;
6313}
6314
6315STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6316{
6317 LogFlowThisFunc(("\n"));
6318
6319 CheckComArgNotNull(aCpuAttached);
6320
6321 *aCpuAttached = false;
6322
6323 AutoCaller autoCaller(this);
6324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6325
6326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6327
6328 /* If hotplug is enabled the CPU is always enabled. */
6329 if (!mHWData->mCPUHotPlugEnabled)
6330 {
6331 if (aCpu < mHWData->mCPUCount)
6332 *aCpuAttached = true;
6333 }
6334 else
6335 {
6336 if (aCpu < SchemaDefs::MaxCPUCount)
6337 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6338 }
6339
6340 return S_OK;
6341}
6342
6343STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6344{
6345 CheckComArgOutPointerValid(aName);
6346
6347 AutoCaller autoCaller(this);
6348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6349
6350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 Utf8Str log = queryLogFilename(aIdx);
6353 if (!RTFileExists(log.c_str()))
6354 log.setNull();
6355 log.cloneTo(aName);
6356
6357 return S_OK;
6358}
6359
6360STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6361{
6362 LogFlowThisFunc(("\n"));
6363 CheckComArgOutSafeArrayPointerValid(aData);
6364 if (aSize < 0)
6365 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6366
6367 AutoCaller autoCaller(this);
6368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6369
6370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6371
6372 HRESULT rc = S_OK;
6373 Utf8Str log = queryLogFilename(aIdx);
6374
6375 /* do not unnecessarily hold the lock while doing something which does
6376 * not need the lock and potentially takes a long time. */
6377 alock.release();
6378
6379 /* Limit the chunk size to 32K for now, as that gives better performance
6380 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6381 * One byte expands to approx. 25 bytes of breathtaking XML. */
6382 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6383 com::SafeArray<BYTE> logData(cbData);
6384
6385 RTFILE LogFile;
6386 int vrc = RTFileOpen(&LogFile, log.c_str(),
6387 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6388 if (RT_SUCCESS(vrc))
6389 {
6390 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6391 if (RT_SUCCESS(vrc))
6392 logData.resize(cbData);
6393 else
6394 rc = setError(VBOX_E_IPRT_ERROR,
6395 tr("Could not read log file '%s' (%Rrc)"),
6396 log.c_str(), vrc);
6397 RTFileClose(LogFile);
6398 }
6399 else
6400 rc = setError(VBOX_E_IPRT_ERROR,
6401 tr("Could not open log file '%s' (%Rrc)"),
6402 log.c_str(), vrc);
6403
6404 if (FAILED(rc))
6405 logData.resize(0);
6406 logData.detachTo(ComSafeArrayOutArg(aData));
6407
6408 return rc;
6409}
6410
6411
6412/**
6413 * Currently this method doesn't attach device to the running VM,
6414 * just makes sure it's plugged on next VM start.
6415 */
6416STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6417{
6418 AutoCaller autoCaller(this);
6419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6420
6421 // lock scope
6422 {
6423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6424
6425 HRESULT rc = checkStateDependency(MutableStateDep);
6426 if (FAILED(rc)) return rc;
6427
6428 ChipsetType_T aChipset = ChipsetType_PIIX3;
6429 COMGETTER(ChipsetType)(&aChipset);
6430
6431 if (aChipset != ChipsetType_ICH9)
6432 {
6433 return setError(E_INVALIDARG,
6434 tr("Host PCI attachment only supported with ICH9 chipset"));
6435 }
6436
6437 // check if device with this host PCI address already attached
6438 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6439 it != mHWData->mPciDeviceAssignments.end();
6440 ++it)
6441 {
6442 LONG iHostAddress = -1;
6443 ComPtr<PciDeviceAttachment> pAttach;
6444 pAttach = *it;
6445 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6446 if (iHostAddress == hostAddress)
6447 return setError(E_INVALIDARG,
6448 tr("Device with host PCI address already attached to this VM"));
6449 }
6450
6451 ComObjPtr<PciDeviceAttachment> pda;
6452 char name[32];
6453
6454 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6455 Bstr bname(name);
6456 pda.createObject();
6457 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6458 setModified(IsModified_MachineData);
6459 mHWData.backup();
6460 mHWData->mPciDeviceAssignments.push_back(pda);
6461 }
6462
6463 return S_OK;
6464}
6465
6466/**
6467 * Currently this method doesn't detach device from the running VM,
6468 * just makes sure it's not plugged on next VM start.
6469 */
6470STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress)
6471{
6472 AutoCaller autoCaller(this);
6473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6474
6475 ComObjPtr<PciDeviceAttachment> pAttach;
6476 bool fRemoved = false;
6477 HRESULT rc;
6478
6479 // lock scope
6480 {
6481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6482
6483 rc = checkStateDependency(MutableStateDep);
6484 if (FAILED(rc)) return rc;
6485
6486 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6487 it != mHWData->mPciDeviceAssignments.end();
6488 ++it)
6489 {
6490 LONG iHostAddress = -1;
6491 pAttach = *it;
6492 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6493 if (iHostAddress != -1 && iHostAddress == hostAddress)
6494 {
6495 setModified(IsModified_MachineData);
6496 mHWData.backup();
6497 mHWData->mPciDeviceAssignments.remove(pAttach);
6498 fRemoved = true;
6499 break;
6500 }
6501 }
6502 }
6503
6504
6505 /* Fire event outside of the lock */
6506 if (fRemoved)
6507 {
6508 Assert(!pAttach.isNull());
6509 ComPtr<IEventSource> es;
6510 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6511 Assert(SUCCEEDED(rc));
6512 Bstr mid;
6513 rc = this->COMGETTER(Id)(mid.asOutParam());
6514 Assert(SUCCEEDED(rc));
6515 fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6516 }
6517
6518 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6519 tr("No host PCI device %08x attached"),
6520 hostAddress
6521 );
6522}
6523
6524STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
6525{
6526 CheckComArgOutSafeArrayPointerValid(aAssignments);
6527
6528 AutoCaller autoCaller(this);
6529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6530
6531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6532
6533 SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments);
6534 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6535
6536 return S_OK;
6537}
6538
6539STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6540{
6541 CheckComArgOutPointerValid(aBandwidthControl);
6542
6543 AutoCaller autoCaller(this);
6544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6545
6546 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6547
6548 return S_OK;
6549}
6550
6551STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6552{
6553 CheckComArgOutPointerValid(pfEnabled);
6554 AutoCaller autoCaller(this);
6555 HRESULT hrc = autoCaller.rc();
6556 if (SUCCEEDED(hrc))
6557 {
6558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6559 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6560 }
6561 return hrc;
6562}
6563
6564STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6565{
6566 AutoCaller autoCaller(this);
6567 HRESULT hrc = autoCaller.rc();
6568 if (SUCCEEDED(hrc))
6569 {
6570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6571 hrc = checkStateDependency(MutableStateDep);
6572 if (SUCCEEDED(hrc))
6573 {
6574 hrc = mHWData.backupEx();
6575 if (SUCCEEDED(hrc))
6576 {
6577 setModified(IsModified_MachineData);
6578 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6579 }
6580 }
6581 }
6582 return hrc;
6583}
6584
6585STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6586{
6587 CheckComArgOutPointerValid(pbstrConfig);
6588 AutoCaller autoCaller(this);
6589 HRESULT hrc = autoCaller.rc();
6590 if (SUCCEEDED(hrc))
6591 {
6592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6593 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6594 }
6595 return hrc;
6596}
6597
6598STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6599{
6600 CheckComArgStr(bstrConfig);
6601 AutoCaller autoCaller(this);
6602 HRESULT hrc = autoCaller.rc();
6603 if (SUCCEEDED(hrc))
6604 {
6605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6606 hrc = checkStateDependency(MutableStateDep);
6607 if (SUCCEEDED(hrc))
6608 {
6609 hrc = mHWData.backupEx();
6610 if (SUCCEEDED(hrc))
6611 {
6612 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6613 if (SUCCEEDED(hrc))
6614 setModified(IsModified_MachineData);
6615 }
6616 }
6617 }
6618 return hrc;
6619
6620}
6621
6622STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6623{
6624 CheckComArgOutPointerValid(pfAllow);
6625 AutoCaller autoCaller(this);
6626 HRESULT hrc = autoCaller.rc();
6627 if (SUCCEEDED(hrc))
6628 {
6629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6630 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6631 }
6632 return hrc;
6633}
6634
6635STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6636{
6637 AutoCaller autoCaller(this);
6638 HRESULT hrc = autoCaller.rc();
6639 if (SUCCEEDED(hrc))
6640 {
6641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6642 hrc = checkStateDependency(MutableStateDep);
6643 if (SUCCEEDED(hrc))
6644 {
6645 hrc = mHWData.backupEx();
6646 if (SUCCEEDED(hrc))
6647 {
6648 setModified(IsModified_MachineData);
6649 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6650 }
6651 }
6652 }
6653 return hrc;
6654}
6655
6656STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6657{
6658 CheckComArgOutPointerValid(pfEnabled);
6659 AutoCaller autoCaller(this);
6660 HRESULT hrc = autoCaller.rc();
6661 if (SUCCEEDED(hrc))
6662 {
6663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6664 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6665 }
6666 return hrc;
6667}
6668
6669STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6670{
6671 AutoCaller autoCaller(this);
6672 HRESULT hrc = autoCaller.rc();
6673 if (SUCCEEDED(hrc))
6674 {
6675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6676 hrc = checkStateDependency(MutableStateDep);
6677 if ( SUCCEEDED(hrc)
6678 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6679 {
6680 AutostartDb *autostartDb = mParent->getAutostartDb();
6681 int vrc;
6682
6683 if (fEnabled)
6684 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6685 else
6686 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6687
6688 if (RT_SUCCESS(vrc))
6689 {
6690 hrc = mHWData.backupEx();
6691 if (SUCCEEDED(hrc))
6692 {
6693 setModified(IsModified_MachineData);
6694 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6695 }
6696 }
6697 else if (vrc == VERR_NOT_SUPPORTED)
6698 hrc = setError(VBOX_E_NOT_SUPPORTED,
6699 tr("The VM autostart feature is not supported on this platform"));
6700 else if (vrc == VERR_PATH_NOT_FOUND)
6701 hrc = setError(E_FAIL,
6702 tr("The path to the autostart database is not set"));
6703 else
6704 hrc = setError(E_UNEXPECTED,
6705 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6706 fEnabled ? "Adding" : "Removing",
6707 mUserData->s.strName.c_str(), vrc);
6708 }
6709 }
6710 return hrc;
6711}
6712
6713STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6714{
6715 CheckComArgOutPointerValid(puDelay);
6716 AutoCaller autoCaller(this);
6717 HRESULT hrc = autoCaller.rc();
6718 if (SUCCEEDED(hrc))
6719 {
6720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6721 *puDelay = mHWData->mAutostart.uAutostartDelay;
6722 }
6723 return hrc;
6724}
6725
6726STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6727{
6728 AutoCaller autoCaller(this);
6729 HRESULT hrc = autoCaller.rc();
6730 if (SUCCEEDED(hrc))
6731 {
6732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6733 hrc = checkStateDependency(MutableStateDep);
6734 if (SUCCEEDED(hrc))
6735 {
6736 hrc = mHWData.backupEx();
6737 if (SUCCEEDED(hrc))
6738 {
6739 setModified(IsModified_MachineData);
6740 mHWData->mAutostart.uAutostartDelay = uDelay;
6741 }
6742 }
6743 }
6744 return hrc;
6745}
6746
6747STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6748{
6749 CheckComArgOutPointerValid(penmAutostopType);
6750 AutoCaller autoCaller(this);
6751 HRESULT hrc = autoCaller.rc();
6752 if (SUCCEEDED(hrc))
6753 {
6754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6755 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6756 }
6757 return hrc;
6758}
6759
6760STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6761{
6762 AutoCaller autoCaller(this);
6763 HRESULT hrc = autoCaller.rc();
6764 if (SUCCEEDED(hrc))
6765 {
6766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6767 hrc = checkStateDependency(MutableStateDep);
6768 if ( SUCCEEDED(hrc)
6769 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6770 {
6771 AutostartDb *autostartDb = mParent->getAutostartDb();
6772 int vrc;
6773
6774 if (enmAutostopType != AutostopType_Disabled)
6775 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6776 else
6777 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6778
6779 if (RT_SUCCESS(vrc))
6780 {
6781 hrc = mHWData.backupEx();
6782 if (SUCCEEDED(hrc))
6783 {
6784 setModified(IsModified_MachineData);
6785 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6786 }
6787 }
6788 else if (vrc == VERR_NOT_SUPPORTED)
6789 hrc = setError(VBOX_E_NOT_SUPPORTED,
6790 tr("The VM autostop feature is not supported on this platform"));
6791 else if (vrc == VERR_PATH_NOT_FOUND)
6792 hrc = setError(E_FAIL,
6793 tr("The path to the autostart database is not set"));
6794 else
6795 hrc = setError(E_UNEXPECTED,
6796 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6797 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6798 mUserData->s.strName.c_str(), vrc);
6799 }
6800 }
6801 return hrc;
6802}
6803
6804
6805STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6806{
6807 LogFlowFuncEnter();
6808
6809 CheckComArgNotNull(pTarget);
6810 CheckComArgOutPointerValid(pProgress);
6811
6812 /* Convert the options. */
6813 RTCList<CloneOptions_T> optList;
6814 if (options != NULL)
6815 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6816
6817 if (optList.contains(CloneOptions_Link))
6818 {
6819 if (!isSnapshotMachine())
6820 return setError(E_INVALIDARG,
6821 tr("Linked clone can only be created from a snapshot"));
6822 if (mode != CloneMode_MachineState)
6823 return setError(E_INVALIDARG,
6824 tr("Linked clone can only be created for a single machine state"));
6825 }
6826 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6827
6828 AutoCaller autoCaller(this);
6829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6830
6831
6832 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6833
6834 HRESULT rc = pWorker->start(pProgress);
6835
6836 LogFlowFuncLeave();
6837
6838 return rc;
6839}
6840
6841// public methods for internal purposes
6842/////////////////////////////////////////////////////////////////////////////
6843
6844/**
6845 * Adds the given IsModified_* flag to the dirty flags of the machine.
6846 * This must be called either during loadSettings or under the machine write lock.
6847 * @param fl
6848 */
6849void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6850{
6851 mData->flModifications |= fl;
6852 if (fAllowStateModification && isStateModificationAllowed())
6853 mData->mCurrentStateModified = true;
6854}
6855
6856/**
6857 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6858 * care of the write locking.
6859 *
6860 * @param fModifications The flag to add.
6861 */
6862void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6863{
6864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6865 setModified(fModification, fAllowStateModification);
6866}
6867
6868/**
6869 * Saves the registry entry of this machine to the given configuration node.
6870 *
6871 * @param aEntryNode Node to save the registry entry to.
6872 *
6873 * @note locks this object for reading.
6874 */
6875HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6876{
6877 AutoLimitedCaller autoCaller(this);
6878 AssertComRCReturnRC(autoCaller.rc());
6879
6880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6881
6882 data.uuid = mData->mUuid;
6883 data.strSettingsFile = mData->m_strConfigFile;
6884
6885 return S_OK;
6886}
6887
6888/**
6889 * Calculates the absolute path of the given path taking the directory of the
6890 * machine settings file as the current directory.
6891 *
6892 * @param aPath Path to calculate the absolute path for.
6893 * @param aResult Where to put the result (used only on success, can be the
6894 * same Utf8Str instance as passed in @a aPath).
6895 * @return IPRT result.
6896 *
6897 * @note Locks this object for reading.
6898 */
6899int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6900{
6901 AutoCaller autoCaller(this);
6902 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6903
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905
6906 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6907
6908 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6909
6910 strSettingsDir.stripFilename();
6911 char folder[RTPATH_MAX];
6912 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6913 if (RT_SUCCESS(vrc))
6914 aResult = folder;
6915
6916 return vrc;
6917}
6918
6919/**
6920 * Copies strSource to strTarget, making it relative to the machine folder
6921 * if it is a subdirectory thereof, or simply copying it otherwise.
6922 *
6923 * @param strSource Path to evaluate and copy.
6924 * @param strTarget Buffer to receive target path.
6925 *
6926 * @note Locks this object for reading.
6927 */
6928void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6929 Utf8Str &strTarget)
6930{
6931 AutoCaller autoCaller(this);
6932 AssertComRCReturn(autoCaller.rc(), (void)0);
6933
6934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6935
6936 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6937 // use strTarget as a temporary buffer to hold the machine settings dir
6938 strTarget = mData->m_strConfigFileFull;
6939 strTarget.stripFilename();
6940 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6941 {
6942 // is relative: then append what's left
6943 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6944 // for empty paths (only possible for subdirs) use "." to avoid
6945 // triggering default settings for not present config attributes.
6946 if (strTarget.isEmpty())
6947 strTarget = ".";
6948 }
6949 else
6950 // is not relative: then overwrite
6951 strTarget = strSource;
6952}
6953
6954/**
6955 * Returns the full path to the machine's log folder in the
6956 * \a aLogFolder argument.
6957 */
6958void Machine::getLogFolder(Utf8Str &aLogFolder)
6959{
6960 AutoCaller autoCaller(this);
6961 AssertComRCReturnVoid(autoCaller.rc());
6962
6963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6964
6965 char szTmp[RTPATH_MAX];
6966 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6967 if (RT_SUCCESS(vrc))
6968 {
6969 if (szTmp[0] && !mUserData.isNull())
6970 {
6971 char szTmp2[RTPATH_MAX];
6972 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6973 if (RT_SUCCESS(vrc))
6974 aLogFolder = BstrFmt("%s%c%s",
6975 szTmp2,
6976 RTPATH_DELIMITER,
6977 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6978 }
6979 else
6980 vrc = VERR_PATH_IS_RELATIVE;
6981 }
6982
6983 if (RT_FAILURE(vrc))
6984 {
6985 // fallback if VBOX_USER_LOGHOME is not set or invalid
6986 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6987 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6988 aLogFolder.append(RTPATH_DELIMITER);
6989 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6990 }
6991}
6992
6993/**
6994 * Returns the full path to the machine's log file for an given index.
6995 */
6996Utf8Str Machine::queryLogFilename(ULONG idx)
6997{
6998 Utf8Str logFolder;
6999 getLogFolder(logFolder);
7000 Assert(logFolder.length());
7001 Utf8Str log;
7002 if (idx == 0)
7003 log = Utf8StrFmt("%s%cVBox.log",
7004 logFolder.c_str(), RTPATH_DELIMITER);
7005 else
7006 log = Utf8StrFmt("%s%cVBox.log.%d",
7007 logFolder.c_str(), RTPATH_DELIMITER, idx);
7008 return log;
7009}
7010
7011/**
7012 * Composes a unique saved state filename based on the current system time. The filename is
7013 * granular to the second so this will work so long as no more than one snapshot is taken on
7014 * a machine per second.
7015 *
7016 * Before version 4.1, we used this formula for saved state files:
7017 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7018 * which no longer works because saved state files can now be shared between the saved state of the
7019 * "saved" machine and an online snapshot, and the following would cause problems:
7020 * 1) save machine
7021 * 2) create online snapshot from that machine state --> reusing saved state file
7022 * 3) save machine again --> filename would be reused, breaking the online snapshot
7023 *
7024 * So instead we now use a timestamp.
7025 *
7026 * @param str
7027 */
7028void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7029{
7030 AutoCaller autoCaller(this);
7031 AssertComRCReturnVoid(autoCaller.rc());
7032
7033 {
7034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7035 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7036 }
7037
7038 RTTIMESPEC ts;
7039 RTTimeNow(&ts);
7040 RTTIME time;
7041 RTTimeExplode(&time, &ts);
7042
7043 strStateFilePath += RTPATH_DELIMITER;
7044 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7045 time.i32Year, time.u8Month, time.u8MonthDay,
7046 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7047}
7048
7049/**
7050 * @note Locks this object for writing, calls the client process
7051 * (inside the lock).
7052 */
7053HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7054 const Utf8Str &strType,
7055 const Utf8Str &strEnvironment,
7056 ProgressProxy *aProgress)
7057{
7058 LogFlowThisFuncEnter();
7059
7060 AssertReturn(aControl, E_FAIL);
7061 AssertReturn(aProgress, E_FAIL);
7062
7063 AutoCaller autoCaller(this);
7064 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7065
7066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7067
7068 if (!mData->mRegistered)
7069 return setError(E_UNEXPECTED,
7070 tr("The machine '%s' is not registered"),
7071 mUserData->s.strName.c_str());
7072
7073 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7074
7075 if ( mData->mSession.mState == SessionState_Locked
7076 || mData->mSession.mState == SessionState_Spawning
7077 || mData->mSession.mState == SessionState_Unlocking)
7078 return setError(VBOX_E_INVALID_OBJECT_STATE,
7079 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7080 mUserData->s.strName.c_str());
7081
7082 /* may not be busy */
7083 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7084
7085 /* get the path to the executable */
7086 char szPath[RTPATH_MAX];
7087 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7088 size_t sz = strlen(szPath);
7089 szPath[sz++] = RTPATH_DELIMITER;
7090 szPath[sz] = 0;
7091 char *cmd = szPath + sz;
7092 sz = RTPATH_MAX - sz;
7093
7094 int vrc = VINF_SUCCESS;
7095 RTPROCESS pid = NIL_RTPROCESS;
7096
7097 RTENV env = RTENV_DEFAULT;
7098
7099 if (!strEnvironment.isEmpty())
7100 {
7101 char *newEnvStr = NULL;
7102
7103 do
7104 {
7105 /* clone the current environment */
7106 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7107 AssertRCBreakStmt(vrc2, vrc = vrc2);
7108
7109 newEnvStr = RTStrDup(strEnvironment.c_str());
7110 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7111
7112 /* put new variables to the environment
7113 * (ignore empty variable names here since RTEnv API
7114 * intentionally doesn't do that) */
7115 char *var = newEnvStr;
7116 for (char *p = newEnvStr; *p; ++p)
7117 {
7118 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7119 {
7120 *p = '\0';
7121 if (*var)
7122 {
7123 char *val = strchr(var, '=');
7124 if (val)
7125 {
7126 *val++ = '\0';
7127 vrc2 = RTEnvSetEx(env, var, val);
7128 }
7129 else
7130 vrc2 = RTEnvUnsetEx(env, var);
7131 if (RT_FAILURE(vrc2))
7132 break;
7133 }
7134 var = p + 1;
7135 }
7136 }
7137 if (RT_SUCCESS(vrc2) && *var)
7138 vrc2 = RTEnvPutEx(env, var);
7139
7140 AssertRCBreakStmt(vrc2, vrc = vrc2);
7141 }
7142 while (0);
7143
7144 if (newEnvStr != NULL)
7145 RTStrFree(newEnvStr);
7146 }
7147
7148 /* Qt is default */
7149#ifdef VBOX_WITH_QTGUI
7150 if (strType == "gui" || strType == "GUI/Qt")
7151 {
7152# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7153 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7154# else
7155 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7156# endif
7157 Assert(sz >= sizeof(VirtualBox_exe));
7158 strcpy(cmd, VirtualBox_exe);
7159
7160 Utf8Str idStr = mData->mUuid.toString();
7161 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7162 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7163 }
7164#else /* !VBOX_WITH_QTGUI */
7165 if (0)
7166 ;
7167#endif /* VBOX_WITH_QTGUI */
7168
7169 else
7170
7171#ifdef VBOX_WITH_VBOXSDL
7172 if (strType == "sdl" || strType == "GUI/SDL")
7173 {
7174 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7175 Assert(sz >= sizeof(VBoxSDL_exe));
7176 strcpy(cmd, VBoxSDL_exe);
7177
7178 Utf8Str idStr = mData->mUuid.toString();
7179 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7180 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7181 }
7182#else /* !VBOX_WITH_VBOXSDL */
7183 if (0)
7184 ;
7185#endif /* !VBOX_WITH_VBOXSDL */
7186
7187 else
7188
7189#ifdef VBOX_WITH_HEADLESS
7190 if ( strType == "headless"
7191 || strType == "capture"
7192 || strType == "vrdp" /* Deprecated. Same as headless. */
7193 )
7194 {
7195 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7196 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7197 * and a VM works even if the server has not been installed.
7198 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7199 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7200 * differently in 4.0 and 3.x.
7201 */
7202 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7203 Assert(sz >= sizeof(VBoxHeadless_exe));
7204 strcpy(cmd, VBoxHeadless_exe);
7205
7206 Utf8Str idStr = mData->mUuid.toString();
7207 /* Leave space for "--capture" arg. */
7208 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7209 "--startvm", idStr.c_str(),
7210 "--vrde", "config",
7211 0, /* For "--capture". */
7212 0 };
7213 if (strType == "capture")
7214 {
7215 unsigned pos = RT_ELEMENTS(args) - 2;
7216 args[pos] = "--capture";
7217 }
7218 vrc = RTProcCreate(szPath, args, env,
7219#ifdef RT_OS_WINDOWS
7220 RTPROC_FLAGS_NO_WINDOW
7221#else
7222 0
7223#endif
7224 , &pid);
7225 }
7226#else /* !VBOX_WITH_HEADLESS */
7227 if (0)
7228 ;
7229#endif /* !VBOX_WITH_HEADLESS */
7230 else
7231 {
7232 RTEnvDestroy(env);
7233 return setError(E_INVALIDARG,
7234 tr("Invalid session type: '%s'"),
7235 strType.c_str());
7236 }
7237
7238 RTEnvDestroy(env);
7239
7240 if (RT_FAILURE(vrc))
7241 return setError(VBOX_E_IPRT_ERROR,
7242 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7243 mUserData->s.strName.c_str(), vrc);
7244
7245 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7246
7247 /*
7248 * Note that we don't release the lock here before calling the client,
7249 * because it doesn't need to call us back if called with a NULL argument.
7250 * Releasing the lock here is dangerous because we didn't prepare the
7251 * launch data yet, but the client we've just started may happen to be
7252 * too fast and call openSession() that will fail (because of PID, etc.),
7253 * so that the Machine will never get out of the Spawning session state.
7254 */
7255
7256 /* inform the session that it will be a remote one */
7257 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7258 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7259 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7260
7261 if (FAILED(rc))
7262 {
7263 /* restore the session state */
7264 mData->mSession.mState = SessionState_Unlocked;
7265 /* The failure may occur w/o any error info (from RPC), so provide one */
7266 return setError(VBOX_E_VM_ERROR,
7267 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7268 }
7269
7270 /* attach launch data to the machine */
7271 Assert(mData->mSession.mPid == NIL_RTPROCESS);
7272 mData->mSession.mRemoteControls.push_back(aControl);
7273 mData->mSession.mProgress = aProgress;
7274 mData->mSession.mPid = pid;
7275 mData->mSession.mState = SessionState_Spawning;
7276 mData->mSession.mType = strType;
7277
7278 LogFlowThisFuncLeave();
7279 return S_OK;
7280}
7281
7282/**
7283 * Returns @c true if the given machine has an open direct session and returns
7284 * the session machine instance and additional session data (on some platforms)
7285 * if so.
7286 *
7287 * Note that when the method returns @c false, the arguments remain unchanged.
7288 *
7289 * @param aMachine Session machine object.
7290 * @param aControl Direct session control object (optional).
7291 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7292 *
7293 * @note locks this object for reading.
7294 */
7295#if defined(RT_OS_WINDOWS)
7296bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7297 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7298 HANDLE *aIPCSem /*= NULL*/,
7299 bool aAllowClosing /*= false*/)
7300#elif defined(RT_OS_OS2)
7301bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7302 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7303 HMTX *aIPCSem /*= NULL*/,
7304 bool aAllowClosing /*= false*/)
7305#else
7306bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7307 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7308 bool aAllowClosing /*= false*/)
7309#endif
7310{
7311 AutoLimitedCaller autoCaller(this);
7312 AssertComRCReturn(autoCaller.rc(), false);
7313
7314 /* just return false for inaccessible machines */
7315 if (autoCaller.state() != Ready)
7316 return false;
7317
7318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7319
7320 if ( mData->mSession.mState == SessionState_Locked
7321 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7322 )
7323 {
7324 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7325
7326 aMachine = mData->mSession.mMachine;
7327
7328 if (aControl != NULL)
7329 *aControl = mData->mSession.mDirectControl;
7330
7331#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7332 /* Additional session data */
7333 if (aIPCSem != NULL)
7334 *aIPCSem = aMachine->mIPCSem;
7335#endif
7336 return true;
7337 }
7338
7339 return false;
7340}
7341
7342/**
7343 * Returns @c true if the given machine has an spawning direct session and
7344 * returns and additional session data (on some platforms) if so.
7345 *
7346 * Note that when the method returns @c false, the arguments remain unchanged.
7347 *
7348 * @param aPID PID of the spawned direct session process.
7349 *
7350 * @note locks this object for reading.
7351 */
7352#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7353bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7354#else
7355bool Machine::isSessionSpawning()
7356#endif
7357{
7358 AutoLimitedCaller autoCaller(this);
7359 AssertComRCReturn(autoCaller.rc(), false);
7360
7361 /* just return false for inaccessible machines */
7362 if (autoCaller.state() != Ready)
7363 return false;
7364
7365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7366
7367 if (mData->mSession.mState == SessionState_Spawning)
7368 {
7369#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7370 /* Additional session data */
7371 if (aPID != NULL)
7372 {
7373 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
7374 *aPID = mData->mSession.mPid;
7375 }
7376#endif
7377 return true;
7378 }
7379
7380 return false;
7381}
7382
7383/**
7384 * Called from the client watcher thread to check for unexpected client process
7385 * death during Session_Spawning state (e.g. before it successfully opened a
7386 * direct session).
7387 *
7388 * On Win32 and on OS/2, this method is called only when we've got the
7389 * direct client's process termination notification, so it always returns @c
7390 * true.
7391 *
7392 * On other platforms, this method returns @c true if the client process is
7393 * terminated and @c false if it's still alive.
7394 *
7395 * @note Locks this object for writing.
7396 */
7397bool Machine::checkForSpawnFailure()
7398{
7399 AutoCaller autoCaller(this);
7400 if (!autoCaller.isOk())
7401 {
7402 /* nothing to do */
7403 LogFlowThisFunc(("Already uninitialized!\n"));
7404 return true;
7405 }
7406
7407 /* VirtualBox::addProcessToReap() needs a write lock */
7408 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7409
7410 if (mData->mSession.mState != SessionState_Spawning)
7411 {
7412 /* nothing to do */
7413 LogFlowThisFunc(("Not spawning any more!\n"));
7414 return true;
7415 }
7416
7417 HRESULT rc = S_OK;
7418
7419#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7420
7421 /* the process was already unexpectedly terminated, we just need to set an
7422 * error and finalize session spawning */
7423 rc = setError(E_FAIL,
7424 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7425 getName().c_str());
7426#else
7427
7428 /* PID not yet initialized, skip check. */
7429 if (mData->mSession.mPid == NIL_RTPROCESS)
7430 return false;
7431
7432 RTPROCSTATUS status;
7433 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
7434 &status);
7435
7436 if (vrc != VERR_PROCESS_RUNNING)
7437 {
7438 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7439 rc = setError(E_FAIL,
7440 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7441 getName().c_str(), status.iStatus);
7442 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7443 rc = setError(E_FAIL,
7444 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7445 getName().c_str(), status.iStatus);
7446 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7447 rc = setError(E_FAIL,
7448 tr("The virtual machine '%s' has terminated abnormally"),
7449 getName().c_str(), status.iStatus);
7450 else
7451 rc = setError(E_FAIL,
7452 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7453 getName().c_str(), rc);
7454 }
7455
7456#endif
7457
7458 if (FAILED(rc))
7459 {
7460 /* Close the remote session, remove the remote control from the list
7461 * and reset session state to Closed (@note keep the code in sync with
7462 * the relevant part in checkForSpawnFailure()). */
7463
7464 Assert(mData->mSession.mRemoteControls.size() == 1);
7465 if (mData->mSession.mRemoteControls.size() == 1)
7466 {
7467 ErrorInfoKeeper eik;
7468 mData->mSession.mRemoteControls.front()->Uninitialize();
7469 }
7470
7471 mData->mSession.mRemoteControls.clear();
7472 mData->mSession.mState = SessionState_Unlocked;
7473
7474 /* finalize the progress after setting the state */
7475 if (!mData->mSession.mProgress.isNull())
7476 {
7477 mData->mSession.mProgress->notifyComplete(rc);
7478 mData->mSession.mProgress.setNull();
7479 }
7480
7481 mParent->addProcessToReap(mData->mSession.mPid);
7482 mData->mSession.mPid = NIL_RTPROCESS;
7483
7484 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7485 return true;
7486 }
7487
7488 return false;
7489}
7490
7491/**
7492 * Checks whether the machine can be registered. If so, commits and saves
7493 * all settings.
7494 *
7495 * @note Must be called from mParent's write lock. Locks this object and
7496 * children for writing.
7497 */
7498HRESULT Machine::prepareRegister()
7499{
7500 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7501
7502 AutoLimitedCaller autoCaller(this);
7503 AssertComRCReturnRC(autoCaller.rc());
7504
7505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7506
7507 /* wait for state dependents to drop to zero */
7508 ensureNoStateDependencies();
7509
7510 if (!mData->mAccessible)
7511 return setError(VBOX_E_INVALID_OBJECT_STATE,
7512 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7513 mUserData->s.strName.c_str(),
7514 mData->mUuid.toString().c_str());
7515
7516 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7517
7518 if (mData->mRegistered)
7519 return setError(VBOX_E_INVALID_OBJECT_STATE,
7520 tr("The machine '%s' with UUID {%s} is already registered"),
7521 mUserData->s.strName.c_str(),
7522 mData->mUuid.toString().c_str());
7523
7524 HRESULT rc = S_OK;
7525
7526 // Ensure the settings are saved. If we are going to be registered and
7527 // no config file exists yet, create it by calling saveSettings() too.
7528 if ( (mData->flModifications)
7529 || (!mData->pMachineConfigFile->fileExists())
7530 )
7531 {
7532 rc = saveSettings(NULL);
7533 // no need to check whether VirtualBox.xml needs saving too since
7534 // we can't have a machine XML file rename pending
7535 if (FAILED(rc)) return rc;
7536 }
7537
7538 /* more config checking goes here */
7539
7540 if (SUCCEEDED(rc))
7541 {
7542 /* we may have had implicit modifications we want to fix on success */
7543 commit();
7544
7545 mData->mRegistered = true;
7546 }
7547 else
7548 {
7549 /* we may have had implicit modifications we want to cancel on failure*/
7550 rollback(false /* aNotify */);
7551 }
7552
7553 return rc;
7554}
7555
7556/**
7557 * Increases the number of objects dependent on the machine state or on the
7558 * registered state. Guarantees that these two states will not change at least
7559 * until #releaseStateDependency() is called.
7560 *
7561 * Depending on the @a aDepType value, additional state checks may be made.
7562 * These checks will set extended error info on failure. See
7563 * #checkStateDependency() for more info.
7564 *
7565 * If this method returns a failure, the dependency is not added and the caller
7566 * is not allowed to rely on any particular machine state or registration state
7567 * value and may return the failed result code to the upper level.
7568 *
7569 * @param aDepType Dependency type to add.
7570 * @param aState Current machine state (NULL if not interested).
7571 * @param aRegistered Current registered state (NULL if not interested).
7572 *
7573 * @note Locks this object for writing.
7574 */
7575HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7576 MachineState_T *aState /* = NULL */,
7577 BOOL *aRegistered /* = NULL */)
7578{
7579 AutoCaller autoCaller(this);
7580 AssertComRCReturnRC(autoCaller.rc());
7581
7582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7583
7584 HRESULT rc = checkStateDependency(aDepType);
7585 if (FAILED(rc)) return rc;
7586
7587 {
7588 if (mData->mMachineStateChangePending != 0)
7589 {
7590 /* ensureNoStateDependencies() is waiting for state dependencies to
7591 * drop to zero so don't add more. It may make sense to wait a bit
7592 * and retry before reporting an error (since the pending state
7593 * transition should be really quick) but let's just assert for
7594 * now to see if it ever happens on practice. */
7595
7596 AssertFailed();
7597
7598 return setError(E_ACCESSDENIED,
7599 tr("Machine state change is in progress. Please retry the operation later."));
7600 }
7601
7602 ++mData->mMachineStateDeps;
7603 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7604 }
7605
7606 if (aState)
7607 *aState = mData->mMachineState;
7608 if (aRegistered)
7609 *aRegistered = mData->mRegistered;
7610
7611 return S_OK;
7612}
7613
7614/**
7615 * Decreases the number of objects dependent on the machine state.
7616 * Must always complete the #addStateDependency() call after the state
7617 * dependency is no more necessary.
7618 */
7619void Machine::releaseStateDependency()
7620{
7621 AutoCaller autoCaller(this);
7622 AssertComRCReturnVoid(autoCaller.rc());
7623
7624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7625
7626 /* releaseStateDependency() w/o addStateDependency()? */
7627 AssertReturnVoid(mData->mMachineStateDeps != 0);
7628 -- mData->mMachineStateDeps;
7629
7630 if (mData->mMachineStateDeps == 0)
7631 {
7632 /* inform ensureNoStateDependencies() that there are no more deps */
7633 if (mData->mMachineStateChangePending != 0)
7634 {
7635 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7636 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7637 }
7638 }
7639}
7640
7641// protected methods
7642/////////////////////////////////////////////////////////////////////////////
7643
7644/**
7645 * Performs machine state checks based on the @a aDepType value. If a check
7646 * fails, this method will set extended error info, otherwise it will return
7647 * S_OK. It is supposed, that on failure, the caller will immediately return
7648 * the return value of this method to the upper level.
7649 *
7650 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7651 *
7652 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7653 * current state of this machine object allows to change settings of the
7654 * machine (i.e. the machine is not registered, or registered but not running
7655 * and not saved). It is useful to call this method from Machine setters
7656 * before performing any change.
7657 *
7658 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7659 * as for MutableStateDep except that if the machine is saved, S_OK is also
7660 * returned. This is useful in setters which allow changing machine
7661 * properties when it is in the saved state.
7662 *
7663 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7664 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7665 * Aborted).
7666 *
7667 * @param aDepType Dependency type to check.
7668 *
7669 * @note Non Machine based classes should use #addStateDependency() and
7670 * #releaseStateDependency() methods or the smart AutoStateDependency
7671 * template.
7672 *
7673 * @note This method must be called from under this object's read or write
7674 * lock.
7675 */
7676HRESULT Machine::checkStateDependency(StateDependency aDepType)
7677{
7678 switch (aDepType)
7679 {
7680 case AnyStateDep:
7681 {
7682 break;
7683 }
7684 case MutableStateDep:
7685 {
7686 if ( mData->mRegistered
7687 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7688 || ( mData->mMachineState != MachineState_Paused
7689 && mData->mMachineState != MachineState_Running
7690 && mData->mMachineState != MachineState_Aborted
7691 && mData->mMachineState != MachineState_Teleported
7692 && mData->mMachineState != MachineState_PoweredOff
7693 )
7694 )
7695 )
7696 return setError(VBOX_E_INVALID_VM_STATE,
7697 tr("The machine is not mutable (state is %s)"),
7698 Global::stringifyMachineState(mData->mMachineState));
7699 break;
7700 }
7701 case MutableOrSavedStateDep:
7702 {
7703 if ( mData->mRegistered
7704 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7705 || ( mData->mMachineState != MachineState_Paused
7706 && mData->mMachineState != MachineState_Running
7707 && mData->mMachineState != MachineState_Aborted
7708 && mData->mMachineState != MachineState_Teleported
7709 && mData->mMachineState != MachineState_Saved
7710 && mData->mMachineState != MachineState_PoweredOff
7711 )
7712 )
7713 )
7714 return setError(VBOX_E_INVALID_VM_STATE,
7715 tr("The machine is not mutable (state is %s)"),
7716 Global::stringifyMachineState(mData->mMachineState));
7717 break;
7718 }
7719 case OfflineStateDep:
7720 {
7721 if ( mData->mRegistered
7722 && ( !isSessionMachine()
7723 || ( mData->mMachineState != MachineState_PoweredOff
7724 && mData->mMachineState != MachineState_Saved
7725 && mData->mMachineState != MachineState_Aborted
7726 && mData->mMachineState != MachineState_Teleported
7727 )
7728 )
7729 )
7730 return setError(VBOX_E_INVALID_VM_STATE,
7731 tr("The machine is not offline (state is %s)"),
7732 Global::stringifyMachineState(mData->mMachineState));
7733 break;
7734 }
7735 }
7736
7737 return S_OK;
7738}
7739
7740/**
7741 * Helper to initialize all associated child objects and allocate data
7742 * structures.
7743 *
7744 * This method must be called as a part of the object's initialization procedure
7745 * (usually done in the #init() method).
7746 *
7747 * @note Must be called only from #init() or from #registeredInit().
7748 */
7749HRESULT Machine::initDataAndChildObjects()
7750{
7751 AutoCaller autoCaller(this);
7752 AssertComRCReturnRC(autoCaller.rc());
7753 AssertComRCReturn(autoCaller.state() == InInit ||
7754 autoCaller.state() == Limited, E_FAIL);
7755
7756 AssertReturn(!mData->mAccessible, E_FAIL);
7757
7758 /* allocate data structures */
7759 mSSData.allocate();
7760 mUserData.allocate();
7761 mHWData.allocate();
7762 mMediaData.allocate();
7763 mStorageControllers.allocate();
7764
7765 /* initialize mOSTypeId */
7766 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7767
7768 /* create associated BIOS settings object */
7769 unconst(mBIOSSettings).createObject();
7770 mBIOSSettings->init(this);
7771
7772 /* create an associated VRDE object (default is disabled) */
7773 unconst(mVRDEServer).createObject();
7774 mVRDEServer->init(this);
7775
7776 /* create associated serial port objects */
7777 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7778 {
7779 unconst(mSerialPorts[slot]).createObject();
7780 mSerialPorts[slot]->init(this, slot);
7781 }
7782
7783 /* create associated parallel port objects */
7784 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7785 {
7786 unconst(mParallelPorts[slot]).createObject();
7787 mParallelPorts[slot]->init(this, slot);
7788 }
7789
7790 /* create the audio adapter object (always present, default is disabled) */
7791 unconst(mAudioAdapter).createObject();
7792 mAudioAdapter->init(this);
7793
7794 /* create the USB controller object (always present, default is disabled) */
7795 unconst(mUSBController).createObject();
7796 mUSBController->init(this);
7797
7798 /* create associated network adapter objects */
7799 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7800 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7801 {
7802 unconst(mNetworkAdapters[slot]).createObject();
7803 mNetworkAdapters[slot]->init(this, slot);
7804 }
7805
7806 /* create the bandwidth control */
7807 unconst(mBandwidthControl).createObject();
7808 mBandwidthControl->init(this);
7809
7810 return S_OK;
7811}
7812
7813/**
7814 * Helper to uninitialize all associated child objects and to free all data
7815 * structures.
7816 *
7817 * This method must be called as a part of the object's uninitialization
7818 * procedure (usually done in the #uninit() method).
7819 *
7820 * @note Must be called only from #uninit() or from #registeredInit().
7821 */
7822void Machine::uninitDataAndChildObjects()
7823{
7824 AutoCaller autoCaller(this);
7825 AssertComRCReturnVoid(autoCaller.rc());
7826 AssertComRCReturnVoid( autoCaller.state() == InUninit
7827 || autoCaller.state() == Limited);
7828
7829 /* tell all our other child objects we've been uninitialized */
7830 if (mBandwidthControl)
7831 {
7832 mBandwidthControl->uninit();
7833 unconst(mBandwidthControl).setNull();
7834 }
7835
7836 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7837 {
7838 if (mNetworkAdapters[slot])
7839 {
7840 mNetworkAdapters[slot]->uninit();
7841 unconst(mNetworkAdapters[slot]).setNull();
7842 }
7843 }
7844
7845 if (mUSBController)
7846 {
7847 mUSBController->uninit();
7848 unconst(mUSBController).setNull();
7849 }
7850
7851 if (mAudioAdapter)
7852 {
7853 mAudioAdapter->uninit();
7854 unconst(mAudioAdapter).setNull();
7855 }
7856
7857 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7858 {
7859 if (mParallelPorts[slot])
7860 {
7861 mParallelPorts[slot]->uninit();
7862 unconst(mParallelPorts[slot]).setNull();
7863 }
7864 }
7865
7866 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7867 {
7868 if (mSerialPorts[slot])
7869 {
7870 mSerialPorts[slot]->uninit();
7871 unconst(mSerialPorts[slot]).setNull();
7872 }
7873 }
7874
7875 if (mVRDEServer)
7876 {
7877 mVRDEServer->uninit();
7878 unconst(mVRDEServer).setNull();
7879 }
7880
7881 if (mBIOSSettings)
7882 {
7883 mBIOSSettings->uninit();
7884 unconst(mBIOSSettings).setNull();
7885 }
7886
7887 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7888 * instance is uninitialized; SessionMachine instances refer to real
7889 * Machine hard disks). This is necessary for a clean re-initialization of
7890 * the VM after successfully re-checking the accessibility state. Note
7891 * that in case of normal Machine or SnapshotMachine uninitialization (as
7892 * a result of unregistering or deleting the snapshot), outdated hard
7893 * disk attachments will already be uninitialized and deleted, so this
7894 * code will not affect them. */
7895 if ( !!mMediaData
7896 && (!isSessionMachine())
7897 )
7898 {
7899 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7900 it != mMediaData->mAttachments.end();
7901 ++it)
7902 {
7903 ComObjPtr<Medium> hd = (*it)->getMedium();
7904 if (hd.isNull())
7905 continue;
7906 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7907 AssertComRC(rc);
7908 }
7909 }
7910
7911 if (!isSessionMachine() && !isSnapshotMachine())
7912 {
7913 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7914 if (mData->mFirstSnapshot)
7915 {
7916 // snapshots tree is protected by media write lock; strictly
7917 // this isn't necessary here since we're deleting the entire
7918 // machine, but otherwise we assert in Snapshot::uninit()
7919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7920 mData->mFirstSnapshot->uninit();
7921 mData->mFirstSnapshot.setNull();
7922 }
7923
7924 mData->mCurrentSnapshot.setNull();
7925 }
7926
7927 /* free data structures (the essential mData structure is not freed here
7928 * since it may be still in use) */
7929 mMediaData.free();
7930 mStorageControllers.free();
7931 mHWData.free();
7932 mUserData.free();
7933 mSSData.free();
7934}
7935
7936/**
7937 * Returns a pointer to the Machine object for this machine that acts like a
7938 * parent for complex machine data objects such as shared folders, etc.
7939 *
7940 * For primary Machine objects and for SnapshotMachine objects, returns this
7941 * object's pointer itself. For SessionMachine objects, returns the peer
7942 * (primary) machine pointer.
7943 */
7944Machine* Machine::getMachine()
7945{
7946 if (isSessionMachine())
7947 return (Machine*)mPeer;
7948 return this;
7949}
7950
7951/**
7952 * Makes sure that there are no machine state dependents. If necessary, waits
7953 * for the number of dependents to drop to zero.
7954 *
7955 * Make sure this method is called from under this object's write lock to
7956 * guarantee that no new dependents may be added when this method returns
7957 * control to the caller.
7958 *
7959 * @note Locks this object for writing. The lock will be released while waiting
7960 * (if necessary).
7961 *
7962 * @warning To be used only in methods that change the machine state!
7963 */
7964void Machine::ensureNoStateDependencies()
7965{
7966 AssertReturnVoid(isWriteLockOnCurrentThread());
7967
7968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7969
7970 /* Wait for all state dependents if necessary */
7971 if (mData->mMachineStateDeps != 0)
7972 {
7973 /* lazy semaphore creation */
7974 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
7975 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
7976
7977 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
7978 mData->mMachineStateDeps));
7979
7980 ++mData->mMachineStateChangePending;
7981
7982 /* reset the semaphore before waiting, the last dependent will signal
7983 * it */
7984 RTSemEventMultiReset(mData->mMachineStateDepsSem);
7985
7986 alock.release();
7987
7988 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
7989
7990 alock.acquire();
7991
7992 -- mData->mMachineStateChangePending;
7993 }
7994}
7995
7996/**
7997 * Changes the machine state and informs callbacks.
7998 *
7999 * This method is not intended to fail so it either returns S_OK or asserts (and
8000 * returns a failure).
8001 *
8002 * @note Locks this object for writing.
8003 */
8004HRESULT Machine::setMachineState(MachineState_T aMachineState)
8005{
8006 LogFlowThisFuncEnter();
8007 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8008
8009 AutoCaller autoCaller(this);
8010 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8011
8012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8013
8014 /* wait for state dependents to drop to zero */
8015 ensureNoStateDependencies();
8016
8017 if (mData->mMachineState != aMachineState)
8018 {
8019 mData->mMachineState = aMachineState;
8020
8021 RTTimeNow(&mData->mLastStateChange);
8022
8023 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8024 }
8025
8026 LogFlowThisFuncLeave();
8027 return S_OK;
8028}
8029
8030/**
8031 * Searches for a shared folder with the given logical name
8032 * in the collection of shared folders.
8033 *
8034 * @param aName logical name of the shared folder
8035 * @param aSharedFolder where to return the found object
8036 * @param aSetError whether to set the error info if the folder is
8037 * not found
8038 * @return
8039 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8040 *
8041 * @note
8042 * must be called from under the object's lock!
8043 */
8044HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8045 ComObjPtr<SharedFolder> &aSharedFolder,
8046 bool aSetError /* = false */)
8047{
8048 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8049 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8050 it != mHWData->mSharedFolders.end();
8051 ++it)
8052 {
8053 SharedFolder *pSF = *it;
8054 AutoCaller autoCaller(pSF);
8055 if (pSF->getName() == aName)
8056 {
8057 aSharedFolder = pSF;
8058 rc = S_OK;
8059 break;
8060 }
8061 }
8062
8063 if (aSetError && FAILED(rc))
8064 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8065
8066 return rc;
8067}
8068
8069/**
8070 * Initializes all machine instance data from the given settings structures
8071 * from XML. The exception is the machine UUID which needs special handling
8072 * depending on the caller's use case, so the caller needs to set that herself.
8073 *
8074 * This gets called in several contexts during machine initialization:
8075 *
8076 * -- When machine XML exists on disk already and needs to be loaded into memory,
8077 * for example, from registeredInit() to load all registered machines on
8078 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8079 * attached to the machine should be part of some media registry already.
8080 *
8081 * -- During OVF import, when a machine config has been constructed from an
8082 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8083 * ensure that the media listed as attachments in the config (which have
8084 * been imported from the OVF) receive the correct registry ID.
8085 *
8086 * -- During VM cloning.
8087 *
8088 * @param config Machine settings from XML.
8089 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8090 * @return
8091 */
8092HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8093 const Guid *puuidRegistry)
8094{
8095 // copy name, description, OS type, teleporter, UTC etc.
8096 mUserData->s = config.machineUserData;
8097
8098 // look up the object by Id to check it is valid
8099 ComPtr<IGuestOSType> guestOSType;
8100 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8101 guestOSType.asOutParam());
8102 if (FAILED(rc)) return rc;
8103
8104 // stateFile (optional)
8105 if (config.strStateFile.isEmpty())
8106 mSSData->strStateFilePath.setNull();
8107 else
8108 {
8109 Utf8Str stateFilePathFull(config.strStateFile);
8110 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8111 if (RT_FAILURE(vrc))
8112 return setError(E_FAIL,
8113 tr("Invalid saved state file path '%s' (%Rrc)"),
8114 config.strStateFile.c_str(),
8115 vrc);
8116 mSSData->strStateFilePath = stateFilePathFull;
8117 }
8118
8119 // snapshot folder needs special processing so set it again
8120 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8121 if (FAILED(rc)) return rc;
8122
8123 /* Copy the extra data items (Not in any case config is already the same as
8124 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8125 * make sure the extra data map is copied). */
8126 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8127
8128 /* currentStateModified (optional, default is true) */
8129 mData->mCurrentStateModified = config.fCurrentStateModified;
8130
8131 mData->mLastStateChange = config.timeLastStateChange;
8132
8133 /*
8134 * note: all mUserData members must be assigned prior this point because
8135 * we need to commit changes in order to let mUserData be shared by all
8136 * snapshot machine instances.
8137 */
8138 mUserData.commitCopy();
8139
8140 // machine registry, if present (must be loaded before snapshots)
8141 if (config.canHaveOwnMediaRegistry())
8142 {
8143 // determine machine folder
8144 Utf8Str strMachineFolder = getSettingsFileFull();
8145 strMachineFolder.stripFilename();
8146 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8147 config.mediaRegistry,
8148 strMachineFolder);
8149 if (FAILED(rc)) return rc;
8150 }
8151
8152 /* Snapshot node (optional) */
8153 size_t cRootSnapshots;
8154 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8155 {
8156 // there must be only one root snapshot
8157 Assert(cRootSnapshots == 1);
8158
8159 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8160
8161 rc = loadSnapshot(snap,
8162 config.uuidCurrentSnapshot,
8163 NULL); // no parent == first snapshot
8164 if (FAILED(rc)) return rc;
8165 }
8166
8167 // hardware data
8168 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8169 if (FAILED(rc)) return rc;
8170
8171 // load storage controllers
8172 rc = loadStorageControllers(config.storageMachine,
8173 puuidRegistry,
8174 NULL /* puuidSnapshot */);
8175 if (FAILED(rc)) return rc;
8176
8177 /*
8178 * NOTE: the assignment below must be the last thing to do,
8179 * otherwise it will be not possible to change the settings
8180 * somewhere in the code above because all setters will be
8181 * blocked by checkStateDependency(MutableStateDep).
8182 */
8183
8184 /* set the machine state to Aborted or Saved when appropriate */
8185 if (config.fAborted)
8186 {
8187 mSSData->strStateFilePath.setNull();
8188
8189 /* no need to use setMachineState() during init() */
8190 mData->mMachineState = MachineState_Aborted;
8191 }
8192 else if (!mSSData->strStateFilePath.isEmpty())
8193 {
8194 /* no need to use setMachineState() during init() */
8195 mData->mMachineState = MachineState_Saved;
8196 }
8197
8198 // after loading settings, we are no longer different from the XML on disk
8199 mData->flModifications = 0;
8200
8201 return S_OK;
8202}
8203
8204/**
8205 * Recursively loads all snapshots starting from the given.
8206 *
8207 * @param aNode <Snapshot> node.
8208 * @param aCurSnapshotId Current snapshot ID from the settings file.
8209 * @param aParentSnapshot Parent snapshot.
8210 */
8211HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8212 const Guid &aCurSnapshotId,
8213 Snapshot *aParentSnapshot)
8214{
8215 AssertReturn(!isSnapshotMachine(), E_FAIL);
8216 AssertReturn(!isSessionMachine(), E_FAIL);
8217
8218 HRESULT rc = S_OK;
8219
8220 Utf8Str strStateFile;
8221 if (!data.strStateFile.isEmpty())
8222 {
8223 /* optional */
8224 strStateFile = data.strStateFile;
8225 int vrc = calculateFullPath(strStateFile, strStateFile);
8226 if (RT_FAILURE(vrc))
8227 return setError(E_FAIL,
8228 tr("Invalid saved state file path '%s' (%Rrc)"),
8229 strStateFile.c_str(),
8230 vrc);
8231 }
8232
8233 /* create a snapshot machine object */
8234 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8235 pSnapshotMachine.createObject();
8236 rc = pSnapshotMachine->initFromSettings(this,
8237 data.hardware,
8238 &data.debugging,
8239 &data.autostart,
8240 data.storage,
8241 data.uuid.ref(),
8242 strStateFile);
8243 if (FAILED(rc)) return rc;
8244
8245 /* create a snapshot object */
8246 ComObjPtr<Snapshot> pSnapshot;
8247 pSnapshot.createObject();
8248 /* initialize the snapshot */
8249 rc = pSnapshot->init(mParent, // VirtualBox object
8250 data.uuid,
8251 data.strName,
8252 data.strDescription,
8253 data.timestamp,
8254 pSnapshotMachine,
8255 aParentSnapshot);
8256 if (FAILED(rc)) return rc;
8257
8258 /* memorize the first snapshot if necessary */
8259 if (!mData->mFirstSnapshot)
8260 mData->mFirstSnapshot = pSnapshot;
8261
8262 /* memorize the current snapshot when appropriate */
8263 if ( !mData->mCurrentSnapshot
8264 && pSnapshot->getId() == aCurSnapshotId
8265 )
8266 mData->mCurrentSnapshot = pSnapshot;
8267
8268 // now create the children
8269 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8270 it != data.llChildSnapshots.end();
8271 ++it)
8272 {
8273 const settings::Snapshot &childData = *it;
8274 // recurse
8275 rc = loadSnapshot(childData,
8276 aCurSnapshotId,
8277 pSnapshot); // parent = the one we created above
8278 if (FAILED(rc)) return rc;
8279 }
8280
8281 return rc;
8282}
8283
8284/**
8285 * Loads settings into mHWData.
8286 *
8287 * @param data Reference to the hardware settings.
8288 * @param pDbg Pointer to the debugging settings.
8289 * @param pAutostart Pointer to the autostart settings.
8290 */
8291HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8292 const settings::Autostart *pAutostart)
8293{
8294 AssertReturn(!isSessionMachine(), E_FAIL);
8295
8296 HRESULT rc = S_OK;
8297
8298 try
8299 {
8300 /* The hardware version attribute (optional). */
8301 mHWData->mHWVersion = data.strVersion;
8302 mHWData->mHardwareUUID = data.uuid;
8303
8304 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8305 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8306 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8307 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8308 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8309 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8310 mHWData->mPAEEnabled = data.fPAE;
8311 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8312
8313 mHWData->mCPUCount = data.cCPUs;
8314 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8315 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8316
8317 // cpu
8318 if (mHWData->mCPUHotPlugEnabled)
8319 {
8320 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8321 it != data.llCpus.end();
8322 ++it)
8323 {
8324 const settings::Cpu &cpu = *it;
8325
8326 mHWData->mCPUAttached[cpu.ulId] = true;
8327 }
8328 }
8329
8330 // cpuid leafs
8331 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8332 it != data.llCpuIdLeafs.end();
8333 ++it)
8334 {
8335 const settings::CpuIdLeaf &leaf = *it;
8336
8337 switch (leaf.ulId)
8338 {
8339 case 0x0:
8340 case 0x1:
8341 case 0x2:
8342 case 0x3:
8343 case 0x4:
8344 case 0x5:
8345 case 0x6:
8346 case 0x7:
8347 case 0x8:
8348 case 0x9:
8349 case 0xA:
8350 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8351 break;
8352
8353 case 0x80000000:
8354 case 0x80000001:
8355 case 0x80000002:
8356 case 0x80000003:
8357 case 0x80000004:
8358 case 0x80000005:
8359 case 0x80000006:
8360 case 0x80000007:
8361 case 0x80000008:
8362 case 0x80000009:
8363 case 0x8000000A:
8364 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8365 break;
8366
8367 default:
8368 /* just ignore */
8369 break;
8370 }
8371 }
8372
8373 mHWData->mMemorySize = data.ulMemorySizeMB;
8374 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8375
8376 // boot order
8377 for (size_t i = 0;
8378 i < RT_ELEMENTS(mHWData->mBootOrder);
8379 i++)
8380 {
8381 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8382 if (it == data.mapBootOrder.end())
8383 mHWData->mBootOrder[i] = DeviceType_Null;
8384 else
8385 mHWData->mBootOrder[i] = it->second;
8386 }
8387
8388 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8389 mHWData->mMonitorCount = data.cMonitors;
8390 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8391 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8392 mHWData->mFirmwareType = data.firmwareType;
8393 mHWData->mPointingHidType = data.pointingHidType;
8394 mHWData->mKeyboardHidType = data.keyboardHidType;
8395 mHWData->mChipsetType = data.chipsetType;
8396 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8397 mHWData->mHpetEnabled = data.fHpetEnabled;
8398
8399 /* VRDEServer */
8400 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8401 if (FAILED(rc)) return rc;
8402
8403 /* BIOS */
8404 rc = mBIOSSettings->loadSettings(data.biosSettings);
8405 if (FAILED(rc)) return rc;
8406
8407 // Bandwidth control (must come before network adapters)
8408 rc = mBandwidthControl->loadSettings(data.ioSettings);
8409 if (FAILED(rc)) return rc;
8410
8411 /* USB Controller */
8412 rc = mUSBController->loadSettings(data.usbController);
8413 if (FAILED(rc)) return rc;
8414
8415 // network adapters
8416 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8417 uint32_t oldCount = mNetworkAdapters.size();
8418 if (newCount > oldCount)
8419 {
8420 mNetworkAdapters.resize(newCount);
8421 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8422 {
8423 unconst(mNetworkAdapters[slot]).createObject();
8424 mNetworkAdapters[slot]->init(this, slot);
8425 }
8426 }
8427 else if (newCount < oldCount)
8428 mNetworkAdapters.resize(newCount);
8429 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8430 it != data.llNetworkAdapters.end();
8431 ++it)
8432 {
8433 const settings::NetworkAdapter &nic = *it;
8434
8435 /* slot unicity is guaranteed by XML Schema */
8436 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8437 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8438 if (FAILED(rc)) return rc;
8439 }
8440
8441 // serial ports
8442 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8443 it != data.llSerialPorts.end();
8444 ++it)
8445 {
8446 const settings::SerialPort &s = *it;
8447
8448 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8449 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8450 if (FAILED(rc)) return rc;
8451 }
8452
8453 // parallel ports (optional)
8454 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8455 it != data.llParallelPorts.end();
8456 ++it)
8457 {
8458 const settings::ParallelPort &p = *it;
8459
8460 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8461 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8462 if (FAILED(rc)) return rc;
8463 }
8464
8465 /* AudioAdapter */
8466 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8467 if (FAILED(rc)) return rc;
8468
8469 /* Shared folders */
8470 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8471 it != data.llSharedFolders.end();
8472 ++it)
8473 {
8474 const settings::SharedFolder &sf = *it;
8475
8476 ComObjPtr<SharedFolder> sharedFolder;
8477 /* Check for double entries. Not allowed! */
8478 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8479 if (SUCCEEDED(rc))
8480 return setError(VBOX_E_OBJECT_IN_USE,
8481 tr("Shared folder named '%s' already exists"),
8482 sf.strName.c_str());
8483
8484 /* Create the new shared folder. Don't break on error. This will be
8485 * reported when the machine starts. */
8486 sharedFolder.createObject();
8487 rc = sharedFolder->init(getMachine(),
8488 sf.strName,
8489 sf.strHostPath,
8490 RT_BOOL(sf.fWritable),
8491 RT_BOOL(sf.fAutoMount),
8492 false /* fFailOnError */);
8493 if (FAILED(rc)) return rc;
8494 mHWData->mSharedFolders.push_back(sharedFolder);
8495 }
8496
8497 // Clipboard
8498 mHWData->mClipboardMode = data.clipboardMode;
8499
8500 // drag'n'drop
8501 mHWData->mDragAndDropMode = data.dragAndDropMode;
8502
8503 // guest settings
8504 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8505
8506 // IO settings
8507 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
8508 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
8509
8510 // Host PCI devices
8511 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8512 it != data.pciAttachments.end();
8513 ++it)
8514 {
8515 const settings::HostPciDeviceAttachment &hpda = *it;
8516 ComObjPtr<PciDeviceAttachment> pda;
8517
8518 pda.createObject();
8519 pda->loadSettings(this, hpda);
8520 mHWData->mPciDeviceAssignments.push_back(pda);
8521 }
8522
8523 /*
8524 * (The following isn't really real hardware, but it lives in HWData
8525 * for reasons of convenience.)
8526 */
8527
8528#ifdef VBOX_WITH_GUEST_PROPS
8529 /* Guest properties (optional) */
8530 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8531 it != data.llGuestProperties.end();
8532 ++it)
8533 {
8534 const settings::GuestProperty &prop = *it;
8535 uint32_t fFlags = guestProp::NILFLAG;
8536 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8537 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8538 mHWData->mGuestProperties.push_back(property);
8539 }
8540
8541 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8542#endif /* VBOX_WITH_GUEST_PROPS defined */
8543
8544 rc = loadDebugging(pDbg);
8545 if (FAILED(rc))
8546 return rc;
8547
8548 mHWData->mAutostart = *pAutostart;
8549 }
8550 catch(std::bad_alloc &)
8551 {
8552 return E_OUTOFMEMORY;
8553 }
8554
8555 AssertComRC(rc);
8556 return rc;
8557}
8558
8559/**
8560 * Called from Machine::loadHardware() to load the debugging settings of the
8561 * machine.
8562 *
8563 * @param pDbg Pointer to the settings.
8564 */
8565HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8566{
8567 mHWData->mDebugging = *pDbg;
8568 /* no more processing currently required, this will probably change. */
8569 return S_OK;
8570}
8571
8572/**
8573 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8574 *
8575 * @param data
8576 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8577 * @param puuidSnapshot
8578 * @return
8579 */
8580HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8581 const Guid *puuidRegistry,
8582 const Guid *puuidSnapshot)
8583{
8584 AssertReturn(!isSessionMachine(), E_FAIL);
8585
8586 HRESULT rc = S_OK;
8587
8588 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8589 it != data.llStorageControllers.end();
8590 ++it)
8591 {
8592 const settings::StorageController &ctlData = *it;
8593
8594 ComObjPtr<StorageController> pCtl;
8595 /* Try to find one with the name first. */
8596 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8597 if (SUCCEEDED(rc))
8598 return setError(VBOX_E_OBJECT_IN_USE,
8599 tr("Storage controller named '%s' already exists"),
8600 ctlData.strName.c_str());
8601
8602 pCtl.createObject();
8603 rc = pCtl->init(this,
8604 ctlData.strName,
8605 ctlData.storageBus,
8606 ctlData.ulInstance,
8607 ctlData.fBootable);
8608 if (FAILED(rc)) return rc;
8609
8610 mStorageControllers->push_back(pCtl);
8611
8612 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8613 if (FAILED(rc)) return rc;
8614
8615 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8616 if (FAILED(rc)) return rc;
8617
8618 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8619 if (FAILED(rc)) return rc;
8620
8621 /* Set IDE emulation settings (only for AHCI controller). */
8622 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8623 {
8624 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8625 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8626 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8627 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8628 )
8629 return rc;
8630 }
8631
8632 /* Load the attached devices now. */
8633 rc = loadStorageDevices(pCtl,
8634 ctlData,
8635 puuidRegistry,
8636 puuidSnapshot);
8637 if (FAILED(rc)) return rc;
8638 }
8639
8640 return S_OK;
8641}
8642
8643/**
8644 * Called from loadStorageControllers for a controller's devices.
8645 *
8646 * @param aStorageController
8647 * @param data
8648 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8649 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8650 * @return
8651 */
8652HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8653 const settings::StorageController &data,
8654 const Guid *puuidRegistry,
8655 const Guid *puuidSnapshot)
8656{
8657 HRESULT rc = S_OK;
8658
8659 /* paranoia: detect duplicate attachments */
8660 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8661 it != data.llAttachedDevices.end();
8662 ++it)
8663 {
8664 const settings::AttachedDevice &ad = *it;
8665
8666 for (settings::AttachedDevicesList::const_iterator it2 = it;
8667 it2 != data.llAttachedDevices.end();
8668 ++it2)
8669 {
8670 if (it == it2)
8671 continue;
8672
8673 const settings::AttachedDevice &ad2 = *it2;
8674
8675 if ( ad.lPort == ad2.lPort
8676 && ad.lDevice == ad2.lDevice)
8677 {
8678 return setError(E_FAIL,
8679 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8680 aStorageController->getName().c_str(),
8681 ad.lPort,
8682 ad.lDevice,
8683 mUserData->s.strName.c_str());
8684 }
8685 }
8686 }
8687
8688 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8689 it != data.llAttachedDevices.end();
8690 ++it)
8691 {
8692 const settings::AttachedDevice &dev = *it;
8693 ComObjPtr<Medium> medium;
8694
8695 switch (dev.deviceType)
8696 {
8697 case DeviceType_Floppy:
8698 case DeviceType_DVD:
8699 if (dev.strHostDriveSrc.isNotEmpty())
8700 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8701 else
8702 rc = mParent->findRemoveableMedium(dev.deviceType,
8703 dev.uuid,
8704 false /* fRefresh */,
8705 false /* aSetError */,
8706 medium);
8707 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8708 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8709 rc = S_OK;
8710 break;
8711
8712 case DeviceType_HardDisk:
8713 {
8714 /* find a hard disk by UUID */
8715 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8716 if (FAILED(rc))
8717 {
8718 if (isSnapshotMachine())
8719 {
8720 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8721 // so the user knows that the bad disk is in a snapshot somewhere
8722 com::ErrorInfo info;
8723 return setError(E_FAIL,
8724 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8725 puuidSnapshot->raw(),
8726 info.getText().raw());
8727 }
8728 else
8729 return rc;
8730 }
8731
8732 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8733
8734 if (medium->getType() == MediumType_Immutable)
8735 {
8736 if (isSnapshotMachine())
8737 return setError(E_FAIL,
8738 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8739 "of the virtual machine '%s' ('%s')"),
8740 medium->getLocationFull().c_str(),
8741 dev.uuid.raw(),
8742 puuidSnapshot->raw(),
8743 mUserData->s.strName.c_str(),
8744 mData->m_strConfigFileFull.c_str());
8745
8746 return setError(E_FAIL,
8747 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8748 medium->getLocationFull().c_str(),
8749 dev.uuid.raw(),
8750 mUserData->s.strName.c_str(),
8751 mData->m_strConfigFileFull.c_str());
8752 }
8753
8754 if (medium->getType() == MediumType_MultiAttach)
8755 {
8756 if (isSnapshotMachine())
8757 return setError(E_FAIL,
8758 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8759 "of the virtual machine '%s' ('%s')"),
8760 medium->getLocationFull().c_str(),
8761 dev.uuid.raw(),
8762 puuidSnapshot->raw(),
8763 mUserData->s.strName.c_str(),
8764 mData->m_strConfigFileFull.c_str());
8765
8766 return setError(E_FAIL,
8767 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8768 medium->getLocationFull().c_str(),
8769 dev.uuid.raw(),
8770 mUserData->s.strName.c_str(),
8771 mData->m_strConfigFileFull.c_str());
8772 }
8773
8774 if ( !isSnapshotMachine()
8775 && medium->getChildren().size() != 0
8776 )
8777 return setError(E_FAIL,
8778 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8779 "because it has %d differencing child hard disks"),
8780 medium->getLocationFull().c_str(),
8781 dev.uuid.raw(),
8782 mUserData->s.strName.c_str(),
8783 mData->m_strConfigFileFull.c_str(),
8784 medium->getChildren().size());
8785
8786 if (findAttachment(mMediaData->mAttachments,
8787 medium))
8788 return setError(E_FAIL,
8789 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8790 medium->getLocationFull().c_str(),
8791 dev.uuid.raw(),
8792 mUserData->s.strName.c_str(),
8793 mData->m_strConfigFileFull.c_str());
8794
8795 break;
8796 }
8797
8798 default:
8799 return setError(E_FAIL,
8800 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8801 medium->getLocationFull().c_str(),
8802 mUserData->s.strName.c_str(),
8803 mData->m_strConfigFileFull.c_str());
8804 }
8805
8806 if (FAILED(rc))
8807 break;
8808
8809 /* Bandwidth groups are loaded at this point. */
8810 ComObjPtr<BandwidthGroup> pBwGroup;
8811
8812 if (!dev.strBwGroup.isEmpty())
8813 {
8814 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8815 if (FAILED(rc))
8816 return setError(E_FAIL,
8817 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8818 medium->getLocationFull().c_str(),
8819 dev.strBwGroup.c_str(),
8820 mUserData->s.strName.c_str(),
8821 mData->m_strConfigFileFull.c_str());
8822 pBwGroup->reference();
8823 }
8824
8825 const Bstr controllerName = aStorageController->getName();
8826 ComObjPtr<MediumAttachment> pAttachment;
8827 pAttachment.createObject();
8828 rc = pAttachment->init(this,
8829 medium,
8830 controllerName,
8831 dev.lPort,
8832 dev.lDevice,
8833 dev.deviceType,
8834 false,
8835 dev.fPassThrough,
8836 dev.fTempEject,
8837 dev.fNonRotational,
8838 dev.fDiscard,
8839 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8840 if (FAILED(rc)) break;
8841
8842 /* associate the medium with this machine and snapshot */
8843 if (!medium.isNull())
8844 {
8845 AutoCaller medCaller(medium);
8846 if (FAILED(medCaller.rc())) return medCaller.rc();
8847 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8848
8849 if (isSnapshotMachine())
8850 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8851 else
8852 rc = medium->addBackReference(mData->mUuid);
8853 /* If the medium->addBackReference fails it sets an appropriate
8854 * error message, so no need to do any guesswork here. */
8855
8856 if (puuidRegistry)
8857 // caller wants registry ID to be set on all attached media (OVF import case)
8858 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8859 }
8860
8861 if (FAILED(rc))
8862 break;
8863
8864 /* back up mMediaData to let registeredInit() properly rollback on failure
8865 * (= limited accessibility) */
8866 setModified(IsModified_Storage);
8867 mMediaData.backup();
8868 mMediaData->mAttachments.push_back(pAttachment);
8869 }
8870
8871 return rc;
8872}
8873
8874/**
8875 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8876 *
8877 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8878 * @param aSnapshot where to return the found snapshot
8879 * @param aSetError true to set extended error info on failure
8880 */
8881HRESULT Machine::findSnapshotById(const Guid &aId,
8882 ComObjPtr<Snapshot> &aSnapshot,
8883 bool aSetError /* = false */)
8884{
8885 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8886
8887 if (!mData->mFirstSnapshot)
8888 {
8889 if (aSetError)
8890 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8891 return E_FAIL;
8892 }
8893
8894 if (aId.isEmpty())
8895 aSnapshot = mData->mFirstSnapshot;
8896 else
8897 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8898
8899 if (!aSnapshot)
8900 {
8901 if (aSetError)
8902 return setError(E_FAIL,
8903 tr("Could not find a snapshot with UUID {%s}"),
8904 aId.toString().c_str());
8905 return E_FAIL;
8906 }
8907
8908 return S_OK;
8909}
8910
8911/**
8912 * Returns the snapshot with the given name or fails of no such snapshot.
8913 *
8914 * @param aName snapshot name to find
8915 * @param aSnapshot where to return the found snapshot
8916 * @param aSetError true to set extended error info on failure
8917 */
8918HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8919 ComObjPtr<Snapshot> &aSnapshot,
8920 bool aSetError /* = false */)
8921{
8922 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8923
8924 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8925
8926 if (!mData->mFirstSnapshot)
8927 {
8928 if (aSetError)
8929 return setError(VBOX_E_OBJECT_NOT_FOUND,
8930 tr("This machine does not have any snapshots"));
8931 return VBOX_E_OBJECT_NOT_FOUND;
8932 }
8933
8934 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8935
8936 if (!aSnapshot)
8937 {
8938 if (aSetError)
8939 return setError(VBOX_E_OBJECT_NOT_FOUND,
8940 tr("Could not find a snapshot named '%s'"), strName.c_str());
8941 return VBOX_E_OBJECT_NOT_FOUND;
8942 }
8943
8944 return S_OK;
8945}
8946
8947/**
8948 * Returns a storage controller object with the given name.
8949 *
8950 * @param aName storage controller name to find
8951 * @param aStorageController where to return the found storage controller
8952 * @param aSetError true to set extended error info on failure
8953 */
8954HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8955 ComObjPtr<StorageController> &aStorageController,
8956 bool aSetError /* = false */)
8957{
8958 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8959
8960 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8961 it != mStorageControllers->end();
8962 ++it)
8963 {
8964 if ((*it)->getName() == aName)
8965 {
8966 aStorageController = (*it);
8967 return S_OK;
8968 }
8969 }
8970
8971 if (aSetError)
8972 return setError(VBOX_E_OBJECT_NOT_FOUND,
8973 tr("Could not find a storage controller named '%s'"),
8974 aName.c_str());
8975 return VBOX_E_OBJECT_NOT_FOUND;
8976}
8977
8978HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
8979 MediaData::AttachmentList &atts)
8980{
8981 AutoCaller autoCaller(this);
8982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8983
8984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8985
8986 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
8987 it != mMediaData->mAttachments.end();
8988 ++it)
8989 {
8990 const ComObjPtr<MediumAttachment> &pAtt = *it;
8991
8992 // should never happen, but deal with NULL pointers in the list.
8993 AssertStmt(!pAtt.isNull(), continue);
8994
8995 // getControllerName() needs caller+read lock
8996 AutoCaller autoAttCaller(pAtt);
8997 if (FAILED(autoAttCaller.rc()))
8998 {
8999 atts.clear();
9000 return autoAttCaller.rc();
9001 }
9002 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9003
9004 if (pAtt->getControllerName() == aName)
9005 atts.push_back(pAtt);
9006 }
9007
9008 return S_OK;
9009}
9010
9011/**
9012 * Helper for #saveSettings. Cares about renaming the settings directory and
9013 * file if the machine name was changed and about creating a new settings file
9014 * if this is a new machine.
9015 *
9016 * @note Must be never called directly but only from #saveSettings().
9017 */
9018HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9019{
9020 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9021
9022 HRESULT rc = S_OK;
9023
9024 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9025
9026 /// @todo need to handle primary group change, too
9027
9028 /* attempt to rename the settings file if machine name is changed */
9029 if ( mUserData->s.fNameSync
9030 && mUserData.isBackedUp()
9031 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9032 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9033 )
9034 {
9035 bool dirRenamed = false;
9036 bool fileRenamed = false;
9037
9038 Utf8Str configFile, newConfigFile;
9039 Utf8Str configFilePrev, newConfigFilePrev;
9040 Utf8Str configDir, newConfigDir;
9041
9042 do
9043 {
9044 int vrc = VINF_SUCCESS;
9045
9046 Utf8Str name = mUserData.backedUpData()->s.strName;
9047 Utf8Str newName = mUserData->s.strName;
9048 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9049 if (group == "/")
9050 group.setNull();
9051 Utf8Str newGroup = mUserData->s.llGroups.front();
9052 if (newGroup == "/")
9053 newGroup.setNull();
9054
9055 configFile = mData->m_strConfigFileFull;
9056
9057 /* first, rename the directory if it matches the group and machine name */
9058 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9059 group.c_str(), RTPATH_DELIMITER, name.c_str());
9060 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9061 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9062 configDir = configFile;
9063 configDir.stripFilename();
9064 newConfigDir = configDir;
9065 if ( configDir.length() >= groupPlusName.length()
9066 && configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).equals(groupPlusName.c_str()))
9067 {
9068 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9069 Utf8Str newConfigBaseDir(newConfigDir);
9070 newConfigDir.append(newGroupPlusName);
9071 /* new dir and old dir cannot be equal here because of 'if'
9072 * above and because name != newName */
9073 Assert(configDir != newConfigDir);
9074 if (!fSettingsFileIsNew)
9075 {
9076 /* perform real rename only if the machine is not new */
9077 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9078 if ( vrc == VERR_FILE_NOT_FOUND
9079 || vrc == VERR_PATH_NOT_FOUND)
9080 {
9081 /* create the parent directory, then retry renaming */
9082 Utf8Str parent(newConfigDir);
9083 parent.stripFilename();
9084 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9085 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9086 }
9087 if (RT_FAILURE(vrc))
9088 {
9089 rc = setError(E_FAIL,
9090 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9091 configDir.c_str(),
9092 newConfigDir.c_str(),
9093 vrc);
9094 break;
9095 }
9096 /* delete subdirectories which are no longer needed */
9097 Utf8Str dir(configDir);
9098 dir.stripFilename();
9099 while (dir != newConfigBaseDir && dir != ".")
9100 {
9101 vrc = RTDirRemove(dir.c_str());
9102 if (RT_FAILURE(vrc))
9103 break;
9104 dir.stripFilename();
9105 }
9106 dirRenamed = true;
9107 }
9108 }
9109
9110 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9111 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9112
9113 /* then try to rename the settings file itself */
9114 if (newConfigFile != configFile)
9115 {
9116 /* get the path to old settings file in renamed directory */
9117 configFile = Utf8StrFmt("%s%c%s",
9118 newConfigDir.c_str(),
9119 RTPATH_DELIMITER,
9120 RTPathFilename(configFile.c_str()));
9121 if (!fSettingsFileIsNew)
9122 {
9123 /* perform real rename only if the machine is not new */
9124 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9125 if (RT_FAILURE(vrc))
9126 {
9127 rc = setError(E_FAIL,
9128 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9129 configFile.c_str(),
9130 newConfigFile.c_str(),
9131 vrc);
9132 break;
9133 }
9134 fileRenamed = true;
9135 configFilePrev = configFile;
9136 configFilePrev += "-prev";
9137 newConfigFilePrev = newConfigFile;
9138 newConfigFilePrev += "-prev";
9139 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9140 }
9141 }
9142
9143 // update m_strConfigFileFull amd mConfigFile
9144 mData->m_strConfigFileFull = newConfigFile;
9145 // compute the relative path too
9146 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9147
9148 // store the old and new so that VirtualBox::saveSettings() can update
9149 // the media registry
9150 if ( mData->mRegistered
9151 && configDir != newConfigDir)
9152 {
9153 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9154
9155 if (pfNeedsGlobalSaveSettings)
9156 *pfNeedsGlobalSaveSettings = true;
9157 }
9158
9159 // in the saved state file path, replace the old directory with the new directory
9160 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9161 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9162
9163 // and do the same thing for the saved state file paths of all the online snapshots
9164 if (mData->mFirstSnapshot)
9165 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9166 newConfigDir.c_str());
9167 }
9168 while (0);
9169
9170 if (FAILED(rc))
9171 {
9172 /* silently try to rename everything back */
9173 if (fileRenamed)
9174 {
9175 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9176 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9177 }
9178 if (dirRenamed)
9179 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9180 }
9181
9182 if (FAILED(rc)) return rc;
9183 }
9184
9185 if (fSettingsFileIsNew)
9186 {
9187 /* create a virgin config file */
9188 int vrc = VINF_SUCCESS;
9189
9190 /* ensure the settings directory exists */
9191 Utf8Str path(mData->m_strConfigFileFull);
9192 path.stripFilename();
9193 if (!RTDirExists(path.c_str()))
9194 {
9195 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9196 if (RT_FAILURE(vrc))
9197 {
9198 return setError(E_FAIL,
9199 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9200 path.c_str(),
9201 vrc);
9202 }
9203 }
9204
9205 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9206 path = Utf8Str(mData->m_strConfigFileFull);
9207 RTFILE f = NIL_RTFILE;
9208 vrc = RTFileOpen(&f, path.c_str(),
9209 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9210 if (RT_FAILURE(vrc))
9211 return setError(E_FAIL,
9212 tr("Could not create the settings file '%s' (%Rrc)"),
9213 path.c_str(),
9214 vrc);
9215 RTFileClose(f);
9216 }
9217
9218 return rc;
9219}
9220
9221/**
9222 * Saves and commits machine data, user data and hardware data.
9223 *
9224 * Note that on failure, the data remains uncommitted.
9225 *
9226 * @a aFlags may combine the following flags:
9227 *
9228 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9229 * Used when saving settings after an operation that makes them 100%
9230 * correspond to the settings from the current snapshot.
9231 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9232 * #isReallyModified() returns false. This is necessary for cases when we
9233 * change machine data directly, not through the backup()/commit() mechanism.
9234 * - SaveS_Force: settings will be saved without doing a deep compare of the
9235 * settings structures. This is used when this is called because snapshots
9236 * have changed to avoid the overhead of the deep compare.
9237 *
9238 * @note Must be called from under this object's write lock. Locks children for
9239 * writing.
9240 *
9241 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9242 * initialized to false and that will be set to true by this function if
9243 * the caller must invoke VirtualBox::saveSettings() because the global
9244 * settings have changed. This will happen if a machine rename has been
9245 * saved and the global machine and media registries will therefore need
9246 * updating.
9247 */
9248HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9249 int aFlags /*= 0*/)
9250{
9251 LogFlowThisFuncEnter();
9252
9253 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9254
9255 /* make sure child objects are unable to modify the settings while we are
9256 * saving them */
9257 ensureNoStateDependencies();
9258
9259 AssertReturn(!isSnapshotMachine(),
9260 E_FAIL);
9261
9262 HRESULT rc = S_OK;
9263 bool fNeedsWrite = false;
9264
9265 /* First, prepare to save settings. It will care about renaming the
9266 * settings directory and file if the machine name was changed and about
9267 * creating a new settings file if this is a new machine. */
9268 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9269 if (FAILED(rc)) return rc;
9270
9271 // keep a pointer to the current settings structures
9272 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9273 settings::MachineConfigFile *pNewConfig = NULL;
9274
9275 try
9276 {
9277 // make a fresh one to have everyone write stuff into
9278 pNewConfig = new settings::MachineConfigFile(NULL);
9279 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9280
9281 // now go and copy all the settings data from COM to the settings structures
9282 // (this calles saveSettings() on all the COM objects in the machine)
9283 copyMachineDataToSettings(*pNewConfig);
9284
9285 if (aFlags & SaveS_ResetCurStateModified)
9286 {
9287 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9288 mData->mCurrentStateModified = FALSE;
9289 fNeedsWrite = true; // always, no need to compare
9290 }
9291 else if (aFlags & SaveS_Force)
9292 {
9293 fNeedsWrite = true; // always, no need to compare
9294 }
9295 else
9296 {
9297 if (!mData->mCurrentStateModified)
9298 {
9299 // do a deep compare of the settings that we just saved with the settings
9300 // previously stored in the config file; this invokes MachineConfigFile::operator==
9301 // which does a deep compare of all the settings, which is expensive but less expensive
9302 // than writing out XML in vain
9303 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9304
9305 // could still be modified if any settings changed
9306 mData->mCurrentStateModified = fAnySettingsChanged;
9307
9308 fNeedsWrite = fAnySettingsChanged;
9309 }
9310 else
9311 fNeedsWrite = true;
9312 }
9313
9314 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9315
9316 if (fNeedsWrite)
9317 // now spit it all out!
9318 pNewConfig->write(mData->m_strConfigFileFull);
9319
9320 mData->pMachineConfigFile = pNewConfig;
9321 delete pOldConfig;
9322 commit();
9323
9324 // after saving settings, we are no longer different from the XML on disk
9325 mData->flModifications = 0;
9326 }
9327 catch (HRESULT err)
9328 {
9329 // we assume that error info is set by the thrower
9330 rc = err;
9331
9332 // restore old config
9333 delete pNewConfig;
9334 mData->pMachineConfigFile = pOldConfig;
9335 }
9336 catch (...)
9337 {
9338 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9339 }
9340
9341 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9342 {
9343 /* Fire the data change event, even on failure (since we've already
9344 * committed all data). This is done only for SessionMachines because
9345 * mutable Machine instances are always not registered (i.e. private
9346 * to the client process that creates them) and thus don't need to
9347 * inform callbacks. */
9348 if (isSessionMachine())
9349 mParent->onMachineDataChange(mData->mUuid);
9350 }
9351
9352 LogFlowThisFunc(("rc=%08X\n", rc));
9353 LogFlowThisFuncLeave();
9354 return rc;
9355}
9356
9357/**
9358 * Implementation for saving the machine settings into the given
9359 * settings::MachineConfigFile instance. This copies machine extradata
9360 * from the previous machine config file in the instance data, if any.
9361 *
9362 * This gets called from two locations:
9363 *
9364 * -- Machine::saveSettings(), during the regular XML writing;
9365 *
9366 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9367 * exported to OVF and we write the VirtualBox proprietary XML
9368 * into a <vbox:Machine> tag.
9369 *
9370 * This routine fills all the fields in there, including snapshots, *except*
9371 * for the following:
9372 *
9373 * -- fCurrentStateModified. There is some special logic associated with that.
9374 *
9375 * The caller can then call MachineConfigFile::write() or do something else
9376 * with it.
9377 *
9378 * Caller must hold the machine lock!
9379 *
9380 * This throws XML errors and HRESULT, so the caller must have a catch block!
9381 */
9382void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9383{
9384 // deep copy extradata
9385 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9386
9387 config.uuid = mData->mUuid;
9388
9389 // copy name, description, OS type, teleport, UTC etc.
9390 config.machineUserData = mUserData->s;
9391
9392 if ( mData->mMachineState == MachineState_Saved
9393 || mData->mMachineState == MachineState_Restoring
9394 // when deleting a snapshot we may or may not have a saved state in the current state,
9395 // so let's not assert here please
9396 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9397 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9398 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9399 && (!mSSData->strStateFilePath.isEmpty())
9400 )
9401 )
9402 {
9403 Assert(!mSSData->strStateFilePath.isEmpty());
9404 /* try to make the file name relative to the settings file dir */
9405 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9406 }
9407 else
9408 {
9409 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9410 config.strStateFile.setNull();
9411 }
9412
9413 if (mData->mCurrentSnapshot)
9414 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9415 else
9416 config.uuidCurrentSnapshot.clear();
9417
9418 config.timeLastStateChange = mData->mLastStateChange;
9419 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9420 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9421
9422 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9423 if (FAILED(rc)) throw rc;
9424
9425 rc = saveStorageControllers(config.storageMachine);
9426 if (FAILED(rc)) throw rc;
9427
9428 // save machine's media registry if this is VirtualBox 4.0 or later
9429 if (config.canHaveOwnMediaRegistry())
9430 {
9431 // determine machine folder
9432 Utf8Str strMachineFolder = getSettingsFileFull();
9433 strMachineFolder.stripFilename();
9434 mParent->saveMediaRegistry(config.mediaRegistry,
9435 getId(), // only media with registry ID == machine UUID
9436 strMachineFolder);
9437 // this throws HRESULT
9438 }
9439
9440 // save snapshots
9441 rc = saveAllSnapshots(config);
9442 if (FAILED(rc)) throw rc;
9443}
9444
9445/**
9446 * Saves all snapshots of the machine into the given machine config file. Called
9447 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9448 * @param config
9449 * @return
9450 */
9451HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9452{
9453 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9454
9455 HRESULT rc = S_OK;
9456
9457 try
9458 {
9459 config.llFirstSnapshot.clear();
9460
9461 if (mData->mFirstSnapshot)
9462 {
9463 settings::Snapshot snapNew;
9464 config.llFirstSnapshot.push_back(snapNew);
9465
9466 // get reference to the fresh copy of the snapshot on the list and
9467 // work on that copy directly to avoid excessive copying later
9468 settings::Snapshot &snap = config.llFirstSnapshot.front();
9469
9470 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9471 if (FAILED(rc)) throw rc;
9472 }
9473
9474// if (mType == IsSessionMachine)
9475// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9476
9477 }
9478 catch (HRESULT err)
9479 {
9480 /* we assume that error info is set by the thrower */
9481 rc = err;
9482 }
9483 catch (...)
9484 {
9485 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9486 }
9487
9488 return rc;
9489}
9490
9491/**
9492 * Saves the VM hardware configuration. It is assumed that the
9493 * given node is empty.
9494 *
9495 * @param data Reference to the settings object for the hardware config.
9496 * @param pDbg Pointer to the settings object for the debugging config
9497 * which happens to live in mHWData.
9498 * @param pAutostart Pointer to the settings object for the autostart config
9499 * which happens to live in mHWData.
9500 */
9501HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9502 settings::Autostart *pAutostart)
9503{
9504 HRESULT rc = S_OK;
9505
9506 try
9507 {
9508 /* The hardware version attribute (optional).
9509 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9510 if ( mHWData->mHWVersion == "1"
9511 && mSSData->strStateFilePath.isEmpty()
9512 )
9513 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. */
9514
9515 data.strVersion = mHWData->mHWVersion;
9516 data.uuid = mHWData->mHardwareUUID;
9517
9518 // CPU
9519 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9520 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9521 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9522 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9523 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9524 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9525 data.fPAE = !!mHWData->mPAEEnabled;
9526 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9527
9528 /* Standard and Extended CPUID leafs. */
9529 data.llCpuIdLeafs.clear();
9530 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9531 {
9532 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9533 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9534 }
9535 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9536 {
9537 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9538 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9539 }
9540
9541 data.cCPUs = mHWData->mCPUCount;
9542 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9543 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9544
9545 data.llCpus.clear();
9546 if (data.fCpuHotPlug)
9547 {
9548 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9549 {
9550 if (mHWData->mCPUAttached[idx])
9551 {
9552 settings::Cpu cpu;
9553 cpu.ulId = idx;
9554 data.llCpus.push_back(cpu);
9555 }
9556 }
9557 }
9558
9559 // memory
9560 data.ulMemorySizeMB = mHWData->mMemorySize;
9561 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9562
9563 // firmware
9564 data.firmwareType = mHWData->mFirmwareType;
9565
9566 // HID
9567 data.pointingHidType = mHWData->mPointingHidType;
9568 data.keyboardHidType = mHWData->mKeyboardHidType;
9569
9570 // chipset
9571 data.chipsetType = mHWData->mChipsetType;
9572
9573 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9574
9575 // HPET
9576 data.fHpetEnabled = !!mHWData->mHpetEnabled;
9577
9578 // boot order
9579 data.mapBootOrder.clear();
9580 for (size_t i = 0;
9581 i < RT_ELEMENTS(mHWData->mBootOrder);
9582 ++i)
9583 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9584
9585 // display
9586 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9587 data.cMonitors = mHWData->mMonitorCount;
9588 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9589 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9590
9591 /* VRDEServer settings (optional) */
9592 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9593 if (FAILED(rc)) throw rc;
9594
9595 /* BIOS (required) */
9596 rc = mBIOSSettings->saveSettings(data.biosSettings);
9597 if (FAILED(rc)) throw rc;
9598
9599 /* USB Controller (required) */
9600 rc = mUSBController->saveSettings(data.usbController);
9601 if (FAILED(rc)) throw rc;
9602
9603 /* Network adapters (required) */
9604 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9605 data.llNetworkAdapters.clear();
9606 /* Write out only the nominal number of network adapters for this
9607 * chipset type. Since Machine::commit() hasn't been called there
9608 * may be extra NIC settings in the vector. */
9609 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9610 {
9611 settings::NetworkAdapter nic;
9612 nic.ulSlot = slot;
9613 /* paranoia check... must not be NULL, but must not crash either. */
9614 if (mNetworkAdapters[slot])
9615 {
9616 rc = mNetworkAdapters[slot]->saveSettings(nic);
9617 if (FAILED(rc)) throw rc;
9618
9619 data.llNetworkAdapters.push_back(nic);
9620 }
9621 }
9622
9623 /* Serial ports */
9624 data.llSerialPorts.clear();
9625 for (ULONG slot = 0;
9626 slot < RT_ELEMENTS(mSerialPorts);
9627 ++slot)
9628 {
9629 settings::SerialPort s;
9630 s.ulSlot = slot;
9631 rc = mSerialPorts[slot]->saveSettings(s);
9632 if (FAILED(rc)) return rc;
9633
9634 data.llSerialPorts.push_back(s);
9635 }
9636
9637 /* Parallel ports */
9638 data.llParallelPorts.clear();
9639 for (ULONG slot = 0;
9640 slot < RT_ELEMENTS(mParallelPorts);
9641 ++slot)
9642 {
9643 settings::ParallelPort p;
9644 p.ulSlot = slot;
9645 rc = mParallelPorts[slot]->saveSettings(p);
9646 if (FAILED(rc)) return rc;
9647
9648 data.llParallelPorts.push_back(p);
9649 }
9650
9651 /* Audio adapter */
9652 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9653 if (FAILED(rc)) return rc;
9654
9655 /* Shared folders */
9656 data.llSharedFolders.clear();
9657 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9658 it != mHWData->mSharedFolders.end();
9659 ++it)
9660 {
9661 SharedFolder *pSF = *it;
9662 AutoCaller sfCaller(pSF);
9663 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9664 settings::SharedFolder sf;
9665 sf.strName = pSF->getName();
9666 sf.strHostPath = pSF->getHostPath();
9667 sf.fWritable = !!pSF->isWritable();
9668 sf.fAutoMount = !!pSF->isAutoMounted();
9669
9670 data.llSharedFolders.push_back(sf);
9671 }
9672
9673 // clipboard
9674 data.clipboardMode = mHWData->mClipboardMode;
9675
9676 // drag'n'drop
9677 data.dragAndDropMode = mHWData->mDragAndDropMode;
9678
9679 /* Guest */
9680 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9681
9682 // IO settings
9683 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
9684 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
9685
9686 /* BandwidthControl (required) */
9687 rc = mBandwidthControl->saveSettings(data.ioSettings);
9688 if (FAILED(rc)) throw rc;
9689
9690 /* Host PCI devices */
9691 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
9692 it != mHWData->mPciDeviceAssignments.end();
9693 ++it)
9694 {
9695 ComObjPtr<PciDeviceAttachment> pda = *it;
9696 settings::HostPciDeviceAttachment hpda;
9697
9698 rc = pda->saveSettings(hpda);
9699 if (FAILED(rc)) throw rc;
9700
9701 data.pciAttachments.push_back(hpda);
9702 }
9703
9704
9705 // guest properties
9706 data.llGuestProperties.clear();
9707#ifdef VBOX_WITH_GUEST_PROPS
9708 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9709 it != mHWData->mGuestProperties.end();
9710 ++it)
9711 {
9712 HWData::GuestProperty property = *it;
9713
9714 /* Remove transient guest properties at shutdown unless we
9715 * are saving state */
9716 if ( ( mData->mMachineState == MachineState_PoweredOff
9717 || mData->mMachineState == MachineState_Aborted
9718 || mData->mMachineState == MachineState_Teleported)
9719 && ( property.mFlags & guestProp::TRANSIENT
9720 || property.mFlags & guestProp::TRANSRESET))
9721 continue;
9722 settings::GuestProperty prop;
9723 prop.strName = property.strName;
9724 prop.strValue = property.strValue;
9725 prop.timestamp = property.mTimestamp;
9726 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9727 guestProp::writeFlags(property.mFlags, szFlags);
9728 prop.strFlags = szFlags;
9729
9730 data.llGuestProperties.push_back(prop);
9731 }
9732
9733 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9734 /* I presume this doesn't require a backup(). */
9735 mData->mGuestPropertiesModified = FALSE;
9736#endif /* VBOX_WITH_GUEST_PROPS defined */
9737
9738 *pDbg = mHWData->mDebugging;
9739 *pAutostart = mHWData->mAutostart;
9740 }
9741 catch(std::bad_alloc &)
9742 {
9743 return E_OUTOFMEMORY;
9744 }
9745
9746 AssertComRC(rc);
9747 return rc;
9748}
9749
9750/**
9751 * Saves the storage controller configuration.
9752 *
9753 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9754 */
9755HRESULT Machine::saveStorageControllers(settings::Storage &data)
9756{
9757 data.llStorageControllers.clear();
9758
9759 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9760 it != mStorageControllers->end();
9761 ++it)
9762 {
9763 HRESULT rc;
9764 ComObjPtr<StorageController> pCtl = *it;
9765
9766 settings::StorageController ctl;
9767 ctl.strName = pCtl->getName();
9768 ctl.controllerType = pCtl->getControllerType();
9769 ctl.storageBus = pCtl->getStorageBus();
9770 ctl.ulInstance = pCtl->getInstance();
9771 ctl.fBootable = pCtl->getBootable();
9772
9773 /* Save the port count. */
9774 ULONG portCount;
9775 rc = pCtl->COMGETTER(PortCount)(&portCount);
9776 ComAssertComRCRet(rc, rc);
9777 ctl.ulPortCount = portCount;
9778
9779 /* Save fUseHostIOCache */
9780 BOOL fUseHostIOCache;
9781 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9782 ComAssertComRCRet(rc, rc);
9783 ctl.fUseHostIOCache = !!fUseHostIOCache;
9784
9785 /* Save IDE emulation settings. */
9786 if (ctl.controllerType == StorageControllerType_IntelAhci)
9787 {
9788 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9789 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9790 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9791 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9792 )
9793 ComAssertComRCRet(rc, rc);
9794 }
9795
9796 /* save the devices now. */
9797 rc = saveStorageDevices(pCtl, ctl);
9798 ComAssertComRCRet(rc, rc);
9799
9800 data.llStorageControllers.push_back(ctl);
9801 }
9802
9803 return S_OK;
9804}
9805
9806/**
9807 * Saves the hard disk configuration.
9808 */
9809HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9810 settings::StorageController &data)
9811{
9812 MediaData::AttachmentList atts;
9813
9814 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9815 if (FAILED(rc)) return rc;
9816
9817 data.llAttachedDevices.clear();
9818 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9819 it != atts.end();
9820 ++it)
9821 {
9822 settings::AttachedDevice dev;
9823
9824 MediumAttachment *pAttach = *it;
9825 Medium *pMedium = pAttach->getMedium();
9826
9827 dev.deviceType = pAttach->getType();
9828 dev.lPort = pAttach->getPort();
9829 dev.lDevice = pAttach->getDevice();
9830 if (pMedium)
9831 {
9832 if (pMedium->isHostDrive())
9833 dev.strHostDriveSrc = pMedium->getLocationFull();
9834 else
9835 dev.uuid = pMedium->getId();
9836 dev.fPassThrough = pAttach->getPassthrough();
9837 dev.fTempEject = pAttach->getTempEject();
9838 dev.fDiscard = pAttach->getDiscard();
9839 }
9840
9841 dev.strBwGroup = pAttach->getBandwidthGroup();
9842
9843 data.llAttachedDevices.push_back(dev);
9844 }
9845
9846 return S_OK;
9847}
9848
9849/**
9850 * Saves machine state settings as defined by aFlags
9851 * (SaveSTS_* values).
9852 *
9853 * @param aFlags Combination of SaveSTS_* flags.
9854 *
9855 * @note Locks objects for writing.
9856 */
9857HRESULT Machine::saveStateSettings(int aFlags)
9858{
9859 if (aFlags == 0)
9860 return S_OK;
9861
9862 AutoCaller autoCaller(this);
9863 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9864
9865 /* This object's write lock is also necessary to serialize file access
9866 * (prevent concurrent reads and writes) */
9867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9868
9869 HRESULT rc = S_OK;
9870
9871 Assert(mData->pMachineConfigFile);
9872
9873 try
9874 {
9875 if (aFlags & SaveSTS_CurStateModified)
9876 mData->pMachineConfigFile->fCurrentStateModified = true;
9877
9878 if (aFlags & SaveSTS_StateFilePath)
9879 {
9880 if (!mSSData->strStateFilePath.isEmpty())
9881 /* try to make the file name relative to the settings file dir */
9882 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9883 else
9884 mData->pMachineConfigFile->strStateFile.setNull();
9885 }
9886
9887 if (aFlags & SaveSTS_StateTimeStamp)
9888 {
9889 Assert( mData->mMachineState != MachineState_Aborted
9890 || mSSData->strStateFilePath.isEmpty());
9891
9892 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9893
9894 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9895//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9896 }
9897
9898 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9899 }
9900 catch (...)
9901 {
9902 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9903 }
9904
9905 return rc;
9906}
9907
9908/**
9909 * Ensures that the given medium is added to a media registry. If this machine
9910 * was created with 4.0 or later, then the machine registry is used. Otherwise
9911 * the global VirtualBox media registry is used.
9912 *
9913 * Caller must NOT hold machine lock, media tree or any medium locks!
9914 *
9915 * @param pMedium
9916 */
9917void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9918{
9919 /* Paranoia checks: do not hold machine or media tree locks. */
9920 AssertReturnVoid(!isWriteLockOnCurrentThread());
9921 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9922
9923 ComObjPtr<Medium> pBase;
9924 {
9925 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9926 pBase = pMedium->getBase();
9927 }
9928
9929 /* Paranoia checks: do not hold medium locks. */
9930 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9931 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9932
9933 // decide which medium registry to use now that the medium is attached:
9934 Guid uuid;
9935 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9936 // machine XML is VirtualBox 4.0 or higher:
9937 uuid = getId(); // machine UUID
9938 else
9939 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9940
9941 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9942 mParent->markRegistryModified(uuid);
9943
9944 /* For more complex hard disk structures it can happen that the base
9945 * medium isn't yet associated with any medium registry. Do that now. */
9946 if (pMedium != pBase)
9947 {
9948 if (pBase->addRegistry(uuid, true /* fRecurse */))
9949 mParent->markRegistryModified(uuid);
9950 }
9951}
9952
9953/**
9954 * Creates differencing hard disks for all normal hard disks attached to this
9955 * machine and a new set of attachments to refer to created disks.
9956 *
9957 * Used when taking a snapshot or when deleting the current state. Gets called
9958 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9959 *
9960 * This method assumes that mMediaData contains the original hard disk attachments
9961 * it needs to create diffs for. On success, these attachments will be replaced
9962 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9963 * called to delete created diffs which will also rollback mMediaData and restore
9964 * whatever was backed up before calling this method.
9965 *
9966 * Attachments with non-normal hard disks are left as is.
9967 *
9968 * If @a aOnline is @c false then the original hard disks that require implicit
9969 * diffs will be locked for reading. Otherwise it is assumed that they are
9970 * already locked for writing (when the VM was started). Note that in the latter
9971 * case it is responsibility of the caller to lock the newly created diffs for
9972 * writing if this method succeeds.
9973 *
9974 * @param aProgress Progress object to run (must contain at least as
9975 * many operations left as the number of hard disks
9976 * attached).
9977 * @param aOnline Whether the VM was online prior to this operation.
9978 *
9979 * @note The progress object is not marked as completed, neither on success nor
9980 * on failure. This is a responsibility of the caller.
9981 *
9982 * @note Locks this object for writing.
9983 */
9984HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
9985 ULONG aWeight,
9986 bool aOnline)
9987{
9988 LogFlowThisFunc(("aOnline=%d\n", aOnline));
9989
9990 AutoCaller autoCaller(this);
9991 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9992
9993 AutoMultiWriteLock2 alock(this->lockHandle(),
9994 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9995
9996 /* must be in a protective state because we release the lock below */
9997 AssertReturn( mData->mMachineState == MachineState_Saving
9998 || mData->mMachineState == MachineState_LiveSnapshotting
9999 || mData->mMachineState == MachineState_RestoringSnapshot
10000 || mData->mMachineState == MachineState_DeletingSnapshot
10001 , E_FAIL);
10002
10003 HRESULT rc = S_OK;
10004
10005 MediumLockListMap lockedMediaOffline;
10006 MediumLockListMap *lockedMediaMap;
10007 if (aOnline)
10008 lockedMediaMap = &mData->mSession.mLockedMedia;
10009 else
10010 lockedMediaMap = &lockedMediaOffline;
10011
10012 try
10013 {
10014 if (!aOnline)
10015 {
10016 /* lock all attached hard disks early to detect "in use"
10017 * situations before creating actual diffs */
10018 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10019 it != mMediaData->mAttachments.end();
10020 ++it)
10021 {
10022 MediumAttachment* pAtt = *it;
10023 if (pAtt->getType() == DeviceType_HardDisk)
10024 {
10025 Medium* pMedium = pAtt->getMedium();
10026 Assert(pMedium);
10027
10028 MediumLockList *pMediumLockList(new MediumLockList());
10029 alock.release();
10030 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10031 false /* fMediumLockWrite */,
10032 NULL,
10033 *pMediumLockList);
10034 alock.acquire();
10035 if (FAILED(rc))
10036 {
10037 delete pMediumLockList;
10038 throw rc;
10039 }
10040 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10041 if (FAILED(rc))
10042 {
10043 throw setError(rc,
10044 tr("Collecting locking information for all attached media failed"));
10045 }
10046 }
10047 }
10048
10049 /* Now lock all media. If this fails, nothing is locked. */
10050 alock.release();
10051 rc = lockedMediaMap->Lock();
10052 alock.acquire();
10053 if (FAILED(rc))
10054 {
10055 throw setError(rc,
10056 tr("Locking of attached media failed"));
10057 }
10058 }
10059
10060 /* remember the current list (note that we don't use backup() since
10061 * mMediaData may be already backed up) */
10062 MediaData::AttachmentList atts = mMediaData->mAttachments;
10063
10064 /* start from scratch */
10065 mMediaData->mAttachments.clear();
10066
10067 /* go through remembered attachments and create diffs for normal hard
10068 * disks and attach them */
10069 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10070 it != atts.end();
10071 ++it)
10072 {
10073 MediumAttachment* pAtt = *it;
10074
10075 DeviceType_T devType = pAtt->getType();
10076 Medium* pMedium = pAtt->getMedium();
10077
10078 if ( devType != DeviceType_HardDisk
10079 || pMedium == NULL
10080 || pMedium->getType() != MediumType_Normal)
10081 {
10082 /* copy the attachment as is */
10083
10084 /** @todo the progress object created in Console::TakeSnaphot
10085 * only expects operations for hard disks. Later other
10086 * device types need to show up in the progress as well. */
10087 if (devType == DeviceType_HardDisk)
10088 {
10089 if (pMedium == NULL)
10090 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10091 aWeight); // weight
10092 else
10093 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10094 pMedium->getBase()->getName().c_str()).raw(),
10095 aWeight); // weight
10096 }
10097
10098 mMediaData->mAttachments.push_back(pAtt);
10099 continue;
10100 }
10101
10102 /* need a diff */
10103 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10104 pMedium->getBase()->getName().c_str()).raw(),
10105 aWeight); // weight
10106
10107 Utf8Str strFullSnapshotFolder;
10108 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10109
10110 ComObjPtr<Medium> diff;
10111 diff.createObject();
10112 // store the diff in the same registry as the parent
10113 // (this cannot fail here because we can't create implicit diffs for
10114 // unregistered images)
10115 Guid uuidRegistryParent;
10116 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10117 Assert(fInRegistry); NOREF(fInRegistry);
10118 rc = diff->init(mParent,
10119 pMedium->getPreferredDiffFormat(),
10120 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10121 uuidRegistryParent);
10122 if (FAILED(rc)) throw rc;
10123
10124 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10125 * the push_back? Looks like we're going to release medium with the
10126 * wrong kind of lock (general issue with if we fail anywhere at all)
10127 * and an orphaned VDI in the snapshots folder. */
10128
10129 /* update the appropriate lock list */
10130 MediumLockList *pMediumLockList;
10131 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10132 AssertComRCThrowRC(rc);
10133 if (aOnline)
10134 {
10135 alock.release();
10136 rc = pMediumLockList->Update(pMedium, false);
10137 alock.acquire();
10138 AssertComRCThrowRC(rc);
10139 }
10140
10141 /* release the locks before the potentially lengthy operation */
10142 alock.release();
10143 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10144 pMediumLockList,
10145 NULL /* aProgress */,
10146 true /* aWait */);
10147 alock.acquire();
10148 if (FAILED(rc)) throw rc;
10149
10150 rc = lockedMediaMap->Unlock();
10151 AssertComRCThrowRC(rc);
10152 alock.release();
10153 rc = pMediumLockList->Append(diff, true);
10154 alock.acquire();
10155 AssertComRCThrowRC(rc);
10156 alock.release();
10157 rc = lockedMediaMap->Lock();
10158 alock.acquire();
10159 AssertComRCThrowRC(rc);
10160
10161 rc = diff->addBackReference(mData->mUuid);
10162 AssertComRCThrowRC(rc);
10163
10164 /* add a new attachment */
10165 ComObjPtr<MediumAttachment> attachment;
10166 attachment.createObject();
10167 rc = attachment->init(this,
10168 diff,
10169 pAtt->getControllerName(),
10170 pAtt->getPort(),
10171 pAtt->getDevice(),
10172 DeviceType_HardDisk,
10173 true /* aImplicit */,
10174 false /* aPassthrough */,
10175 false /* aTempEject */,
10176 pAtt->getNonRotational(),
10177 pAtt->getDiscard(),
10178 pAtt->getBandwidthGroup());
10179 if (FAILED(rc)) throw rc;
10180
10181 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10182 AssertComRCThrowRC(rc);
10183 mMediaData->mAttachments.push_back(attachment);
10184 }
10185 }
10186 catch (HRESULT aRC) { rc = aRC; }
10187
10188 /* unlock all hard disks we locked */
10189 if (!aOnline)
10190 {
10191 ErrorInfoKeeper eik;
10192
10193 HRESULT rc1 = lockedMediaMap->Clear();
10194 AssertComRC(rc1);
10195 }
10196
10197 if (FAILED(rc))
10198 {
10199 MultiResult mrc = rc;
10200
10201 alock.release();
10202 mrc = deleteImplicitDiffs();
10203 }
10204
10205 return rc;
10206}
10207
10208/**
10209 * Deletes implicit differencing hard disks created either by
10210 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10211 *
10212 * Note that to delete hard disks created by #AttachDevice() this method is
10213 * called from #fixupMedia() when the changes are rolled back.
10214 *
10215 * @note Locks this object for writing.
10216 */
10217HRESULT Machine::deleteImplicitDiffs()
10218{
10219 AutoCaller autoCaller(this);
10220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10221
10222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10223 LogFlowThisFuncEnter();
10224
10225 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10226
10227 HRESULT rc = S_OK;
10228
10229 MediaData::AttachmentList implicitAtts;
10230
10231 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10232
10233 /* enumerate new attachments */
10234 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10235 it != mMediaData->mAttachments.end();
10236 ++it)
10237 {
10238 ComObjPtr<Medium> hd = (*it)->getMedium();
10239 if (hd.isNull())
10240 continue;
10241
10242 if ((*it)->isImplicit())
10243 {
10244 /* deassociate and mark for deletion */
10245 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10246 rc = hd->removeBackReference(mData->mUuid);
10247 AssertComRC(rc);
10248 implicitAtts.push_back(*it);
10249 continue;
10250 }
10251
10252 /* was this hard disk attached before? */
10253 if (!findAttachment(oldAtts, hd))
10254 {
10255 /* no: de-associate */
10256 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10257 rc = hd->removeBackReference(mData->mUuid);
10258 AssertComRC(rc);
10259 continue;
10260 }
10261 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10262 }
10263
10264 /* rollback hard disk changes */
10265 mMediaData.rollback();
10266
10267 MultiResult mrc(S_OK);
10268
10269 /* delete unused implicit diffs */
10270 if (implicitAtts.size() != 0)
10271 {
10272 /* will release the lock before the potentially lengthy
10273 * operation, so protect with the special state (unless already
10274 * protected) */
10275 MachineState_T oldState = mData->mMachineState;
10276 if ( oldState != MachineState_Saving
10277 && oldState != MachineState_LiveSnapshotting
10278 && oldState != MachineState_RestoringSnapshot
10279 && oldState != MachineState_DeletingSnapshot
10280 && oldState != MachineState_DeletingSnapshotOnline
10281 && oldState != MachineState_DeletingSnapshotPaused
10282 )
10283 setMachineState(MachineState_SettingUp);
10284
10285 alock.release();
10286
10287 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10288 it != implicitAtts.end();
10289 ++it)
10290 {
10291 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10292 ComObjPtr<Medium> hd = (*it)->getMedium();
10293
10294 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10295 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10296 mrc = rc;
10297 }
10298
10299 alock.acquire();
10300
10301 if (mData->mMachineState == MachineState_SettingUp)
10302 setMachineState(oldState);
10303 }
10304
10305 return mrc;
10306}
10307
10308/**
10309 * Looks through the given list of media attachments for one with the given parameters
10310 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10311 * can be searched as well if needed.
10312 *
10313 * @param list
10314 * @param aControllerName
10315 * @param aControllerPort
10316 * @param aDevice
10317 * @return
10318 */
10319MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10320 IN_BSTR aControllerName,
10321 LONG aControllerPort,
10322 LONG aDevice)
10323{
10324 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10325 it != ll.end();
10326 ++it)
10327 {
10328 MediumAttachment *pAttach = *it;
10329 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10330 return pAttach;
10331 }
10332
10333 return NULL;
10334}
10335
10336/**
10337 * Looks through the given list of media attachments for one with the given parameters
10338 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10339 * can be searched as well if needed.
10340 *
10341 * @param list
10342 * @param aControllerName
10343 * @param aControllerPort
10344 * @param aDevice
10345 * @return
10346 */
10347MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10348 ComObjPtr<Medium> pMedium)
10349{
10350 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10351 it != ll.end();
10352 ++it)
10353 {
10354 MediumAttachment *pAttach = *it;
10355 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10356 if (pMediumThis == pMedium)
10357 return pAttach;
10358 }
10359
10360 return NULL;
10361}
10362
10363/**
10364 * Looks through the given list of media attachments for one with the given parameters
10365 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10366 * can be searched as well if needed.
10367 *
10368 * @param list
10369 * @param aControllerName
10370 * @param aControllerPort
10371 * @param aDevice
10372 * @return
10373 */
10374MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10375 Guid &id)
10376{
10377 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10378 it != ll.end();
10379 ++it)
10380 {
10381 MediumAttachment *pAttach = *it;
10382 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10383 if (pMediumThis->getId() == id)
10384 return pAttach;
10385 }
10386
10387 return NULL;
10388}
10389
10390/**
10391 * Main implementation for Machine::DetachDevice. This also gets called
10392 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10393 *
10394 * @param pAttach Medium attachment to detach.
10395 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10396 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10397 * @return
10398 */
10399HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10400 AutoWriteLock &writeLock,
10401 Snapshot *pSnapshot)
10402{
10403 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10404 DeviceType_T mediumType = pAttach->getType();
10405
10406 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10407
10408 if (pAttach->isImplicit())
10409 {
10410 /* attempt to implicitly delete the implicitly created diff */
10411
10412 /// @todo move the implicit flag from MediumAttachment to Medium
10413 /// and forbid any hard disk operation when it is implicit. Or maybe
10414 /// a special media state for it to make it even more simple.
10415
10416 Assert(mMediaData.isBackedUp());
10417
10418 /* will release the lock before the potentially lengthy operation, so
10419 * protect with the special state */
10420 MachineState_T oldState = mData->mMachineState;
10421 setMachineState(MachineState_SettingUp);
10422
10423 writeLock.release();
10424
10425 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10426 true /*aWait*/);
10427
10428 writeLock.acquire();
10429
10430 setMachineState(oldState);
10431
10432 if (FAILED(rc)) return rc;
10433 }
10434
10435 setModified(IsModified_Storage);
10436 mMediaData.backup();
10437 mMediaData->mAttachments.remove(pAttach);
10438
10439 if (!oldmedium.isNull())
10440 {
10441 // if this is from a snapshot, do not defer detachment to commitMedia()
10442 if (pSnapshot)
10443 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10444 // else if non-hard disk media, do not defer detachment to commitMedia() either
10445 else if (mediumType != DeviceType_HardDisk)
10446 oldmedium->removeBackReference(mData->mUuid);
10447 }
10448
10449 return S_OK;
10450}
10451
10452/**
10453 * Goes thru all media of the given list and
10454 *
10455 * 1) calls detachDevice() on each of them for this machine and
10456 * 2) adds all Medium objects found in the process to the given list,
10457 * depending on cleanupMode.
10458 *
10459 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10460 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10461 * media to the list.
10462 *
10463 * This gets called from Machine::Unregister, both for the actual Machine and
10464 * the SnapshotMachine objects that might be found in the snapshots.
10465 *
10466 * Requires caller and locking. The machine lock must be passed in because it
10467 * will be passed on to detachDevice which needs it for temporary unlocking.
10468 *
10469 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10470 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10471 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10472 * otherwise no media get added.
10473 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10474 * @return
10475 */
10476HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10477 Snapshot *pSnapshot,
10478 CleanupMode_T cleanupMode,
10479 MediaList &llMedia)
10480{
10481 Assert(isWriteLockOnCurrentThread());
10482
10483 HRESULT rc;
10484
10485 // make a temporary list because detachDevice invalidates iterators into
10486 // mMediaData->mAttachments
10487 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10488
10489 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10490 it != llAttachments2.end();
10491 ++it)
10492 {
10493 ComObjPtr<MediumAttachment> &pAttach = *it;
10494 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10495
10496 if (!pMedium.isNull())
10497 {
10498 AutoCaller mac(pMedium);
10499 if (FAILED(mac.rc())) return mac.rc();
10500 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10501 DeviceType_T devType = pMedium->getDeviceType();
10502 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10503 && devType == DeviceType_HardDisk)
10504 || (cleanupMode == CleanupMode_Full)
10505 )
10506 {
10507 llMedia.push_back(pMedium);
10508 ComObjPtr<Medium> pParent = pMedium->getParent();
10509 /*
10510 * Search for medias which are not attached to any machine, but
10511 * in the chain to an attached disk. Mediums are only consided
10512 * if they are:
10513 * - have only one child
10514 * - no references to any machines
10515 * - are of normal medium type
10516 */
10517 while (!pParent.isNull())
10518 {
10519 AutoCaller mac1(pParent);
10520 if (FAILED(mac1.rc())) return mac1.rc();
10521 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10522 if (pParent->getChildren().size() == 1)
10523 {
10524 if ( pParent->getMachineBackRefCount() == 0
10525 && pParent->getType() == MediumType_Normal
10526 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10527 llMedia.push_back(pParent);
10528 }
10529 else
10530 break;
10531 pParent = pParent->getParent();
10532 }
10533 }
10534 }
10535
10536 // real machine: then we need to use the proper method
10537 rc = detachDevice(pAttach, writeLock, pSnapshot);
10538
10539 if (FAILED(rc))
10540 return rc;
10541 }
10542
10543 return S_OK;
10544}
10545
10546/**
10547 * Perform deferred hard disk detachments.
10548 *
10549 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10550 * backed up).
10551 *
10552 * If @a aOnline is @c true then this method will also unlock the old hard disks
10553 * for which the new implicit diffs were created and will lock these new diffs for
10554 * writing.
10555 *
10556 * @param aOnline Whether the VM was online prior to this operation.
10557 *
10558 * @note Locks this object for writing!
10559 */
10560void Machine::commitMedia(bool aOnline /*= false*/)
10561{
10562 AutoCaller autoCaller(this);
10563 AssertComRCReturnVoid(autoCaller.rc());
10564
10565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10566
10567 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10568
10569 HRESULT rc = S_OK;
10570
10571 /* no attach/detach operations -- nothing to do */
10572 if (!mMediaData.isBackedUp())
10573 return;
10574
10575 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10576 bool fMediaNeedsLocking = false;
10577
10578 /* enumerate new attachments */
10579 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10580 it != mMediaData->mAttachments.end();
10581 ++it)
10582 {
10583 MediumAttachment *pAttach = *it;
10584
10585 pAttach->commit();
10586
10587 Medium* pMedium = pAttach->getMedium();
10588 bool fImplicit = pAttach->isImplicit();
10589
10590 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10591 (pMedium) ? pMedium->getName().c_str() : "NULL",
10592 fImplicit));
10593
10594 /** @todo convert all this Machine-based voodoo to MediumAttachment
10595 * based commit logic. */
10596 if (fImplicit)
10597 {
10598 /* convert implicit attachment to normal */
10599 pAttach->setImplicit(false);
10600
10601 if ( aOnline
10602 && pMedium
10603 && pAttach->getType() == DeviceType_HardDisk
10604 )
10605 {
10606 ComObjPtr<Medium> parent = pMedium->getParent();
10607 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10608
10609 /* update the appropriate lock list */
10610 MediumLockList *pMediumLockList;
10611 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10612 AssertComRC(rc);
10613 if (pMediumLockList)
10614 {
10615 /* unlock if there's a need to change the locking */
10616 if (!fMediaNeedsLocking)
10617 {
10618 rc = mData->mSession.mLockedMedia.Unlock();
10619 AssertComRC(rc);
10620 fMediaNeedsLocking = true;
10621 }
10622 rc = pMediumLockList->Update(parent, false);
10623 AssertComRC(rc);
10624 rc = pMediumLockList->Append(pMedium, true);
10625 AssertComRC(rc);
10626 }
10627 }
10628
10629 continue;
10630 }
10631
10632 if (pMedium)
10633 {
10634 /* was this medium attached before? */
10635 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10636 oldIt != oldAtts.end();
10637 ++oldIt)
10638 {
10639 MediumAttachment *pOldAttach = *oldIt;
10640 if (pOldAttach->getMedium() == pMedium)
10641 {
10642 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10643
10644 /* yes: remove from old to avoid de-association */
10645 oldAtts.erase(oldIt);
10646 break;
10647 }
10648 }
10649 }
10650 }
10651
10652 /* enumerate remaining old attachments and de-associate from the
10653 * current machine state */
10654 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10655 it != oldAtts.end();
10656 ++it)
10657 {
10658 MediumAttachment *pAttach = *it;
10659 Medium* pMedium = pAttach->getMedium();
10660
10661 /* Detach only hard disks, since DVD/floppy media is detached
10662 * instantly in MountMedium. */
10663 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10664 {
10665 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10666
10667 /* now de-associate from the current machine state */
10668 rc = pMedium->removeBackReference(mData->mUuid);
10669 AssertComRC(rc);
10670
10671 if (aOnline)
10672 {
10673 /* unlock since medium is not used anymore */
10674 MediumLockList *pMediumLockList;
10675 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10676 AssertComRC(rc);
10677 if (pMediumLockList)
10678 {
10679 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10680 AssertComRC(rc);
10681 }
10682 }
10683 }
10684 }
10685
10686 /* take media locks again so that the locking state is consistent */
10687 if (fMediaNeedsLocking)
10688 {
10689 Assert(aOnline);
10690 rc = mData->mSession.mLockedMedia.Lock();
10691 AssertComRC(rc);
10692 }
10693
10694 /* commit the hard disk changes */
10695 mMediaData.commit();
10696
10697 if (isSessionMachine())
10698 {
10699 /*
10700 * Update the parent machine to point to the new owner.
10701 * This is necessary because the stored parent will point to the
10702 * session machine otherwise and cause crashes or errors later
10703 * when the session machine gets invalid.
10704 */
10705 /** @todo Change the MediumAttachment class to behave like any other
10706 * class in this regard by creating peer MediumAttachment
10707 * objects for session machines and share the data with the peer
10708 * machine.
10709 */
10710 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10711 it != mMediaData->mAttachments.end();
10712 ++it)
10713 {
10714 (*it)->updateParentMachine(mPeer);
10715 }
10716
10717 /* attach new data to the primary machine and reshare it */
10718 mPeer->mMediaData.attach(mMediaData);
10719 }
10720
10721 return;
10722}
10723
10724/**
10725 * Perform deferred deletion of implicitly created diffs.
10726 *
10727 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10728 * backed up).
10729 *
10730 * @note Locks this object for writing!
10731 */
10732void Machine::rollbackMedia()
10733{
10734 AutoCaller autoCaller(this);
10735 AssertComRCReturnVoid (autoCaller.rc());
10736
10737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10738
10739 LogFlowThisFunc(("Entering\n"));
10740
10741 HRESULT rc = S_OK;
10742
10743 /* no attach/detach operations -- nothing to do */
10744 if (!mMediaData.isBackedUp())
10745 return;
10746
10747 /* enumerate new attachments */
10748 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10749 it != mMediaData->mAttachments.end();
10750 ++it)
10751 {
10752 MediumAttachment *pAttach = *it;
10753 /* Fix up the backrefs for DVD/floppy media. */
10754 if (pAttach->getType() != DeviceType_HardDisk)
10755 {
10756 Medium* pMedium = pAttach->getMedium();
10757 if (pMedium)
10758 {
10759 rc = pMedium->removeBackReference(mData->mUuid);
10760 AssertComRC(rc);
10761 }
10762 }
10763
10764 (*it)->rollback();
10765
10766 pAttach = *it;
10767 /* Fix up the backrefs for DVD/floppy media. */
10768 if (pAttach->getType() != DeviceType_HardDisk)
10769 {
10770 Medium* pMedium = pAttach->getMedium();
10771 if (pMedium)
10772 {
10773 rc = pMedium->addBackReference(mData->mUuid);
10774 AssertComRC(rc);
10775 }
10776 }
10777 }
10778
10779 /** @todo convert all this Machine-based voodoo to MediumAttachment
10780 * based rollback logic. */
10781 deleteImplicitDiffs();
10782
10783 return;
10784}
10785
10786/**
10787 * Returns true if the settings file is located in the directory named exactly
10788 * as the machine; this means, among other things, that the machine directory
10789 * should be auto-renamed.
10790 *
10791 * @param aSettingsDir if not NULL, the full machine settings file directory
10792 * name will be assigned there.
10793 *
10794 * @note Doesn't lock anything.
10795 * @note Not thread safe (must be called from this object's lock).
10796 */
10797bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10798{
10799 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10800 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10801 if (aSettingsDir)
10802 *aSettingsDir = strMachineDirName;
10803 strMachineDirName.stripPath(); // vmname
10804 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10805 strConfigFileOnly.stripPath() // vmname.vbox
10806 .stripExt(); // vmname
10807
10808 AssertReturn(!strMachineDirName.isEmpty(), false);
10809 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10810
10811 return strMachineDirName == strConfigFileOnly;
10812}
10813
10814/**
10815 * Discards all changes to machine settings.
10816 *
10817 * @param aNotify Whether to notify the direct session about changes or not.
10818 *
10819 * @note Locks objects for writing!
10820 */
10821void Machine::rollback(bool aNotify)
10822{
10823 AutoCaller autoCaller(this);
10824 AssertComRCReturn(autoCaller.rc(), (void)0);
10825
10826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10827
10828 if (!mStorageControllers.isNull())
10829 {
10830 if (mStorageControllers.isBackedUp())
10831 {
10832 /* unitialize all new devices (absent in the backed up list). */
10833 StorageControllerList::const_iterator it = mStorageControllers->begin();
10834 StorageControllerList *backedList = mStorageControllers.backedUpData();
10835 while (it != mStorageControllers->end())
10836 {
10837 if ( std::find(backedList->begin(), backedList->end(), *it)
10838 == backedList->end()
10839 )
10840 {
10841 (*it)->uninit();
10842 }
10843 ++it;
10844 }
10845
10846 /* restore the list */
10847 mStorageControllers.rollback();
10848 }
10849
10850 /* rollback any changes to devices after restoring the list */
10851 if (mData->flModifications & IsModified_Storage)
10852 {
10853 StorageControllerList::const_iterator it = mStorageControllers->begin();
10854 while (it != mStorageControllers->end())
10855 {
10856 (*it)->rollback();
10857 ++it;
10858 }
10859 }
10860 }
10861
10862 mUserData.rollback();
10863
10864 mHWData.rollback();
10865
10866 if (mData->flModifications & IsModified_Storage)
10867 rollbackMedia();
10868
10869 if (mBIOSSettings)
10870 mBIOSSettings->rollback();
10871
10872 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10873 mVRDEServer->rollback();
10874
10875 if (mAudioAdapter)
10876 mAudioAdapter->rollback();
10877
10878 if (mUSBController && (mData->flModifications & IsModified_USB))
10879 mUSBController->rollback();
10880
10881 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10882 mBandwidthControl->rollback();
10883
10884 if (!mHWData.isNull())
10885 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10886 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10887 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10888 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10889
10890 if (mData->flModifications & IsModified_NetworkAdapters)
10891 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10892 if ( mNetworkAdapters[slot]
10893 && mNetworkAdapters[slot]->isModified())
10894 {
10895 mNetworkAdapters[slot]->rollback();
10896 networkAdapters[slot] = mNetworkAdapters[slot];
10897 }
10898
10899 if (mData->flModifications & IsModified_SerialPorts)
10900 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10901 if ( mSerialPorts[slot]
10902 && mSerialPorts[slot]->isModified())
10903 {
10904 mSerialPorts[slot]->rollback();
10905 serialPorts[slot] = mSerialPorts[slot];
10906 }
10907
10908 if (mData->flModifications & IsModified_ParallelPorts)
10909 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10910 if ( mParallelPorts[slot]
10911 && mParallelPorts[slot]->isModified())
10912 {
10913 mParallelPorts[slot]->rollback();
10914 parallelPorts[slot] = mParallelPorts[slot];
10915 }
10916
10917 if (aNotify)
10918 {
10919 /* inform the direct session about changes */
10920
10921 ComObjPtr<Machine> that = this;
10922 uint32_t flModifications = mData->flModifications;
10923 alock.release();
10924
10925 if (flModifications & IsModified_SharedFolders)
10926 that->onSharedFolderChange();
10927
10928 if (flModifications & IsModified_VRDEServer)
10929 that->onVRDEServerChange(/* aRestart */ TRUE);
10930 if (flModifications & IsModified_USB)
10931 that->onUSBControllerChange();
10932
10933 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10934 if (networkAdapters[slot])
10935 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10936 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10937 if (serialPorts[slot])
10938 that->onSerialPortChange(serialPorts[slot]);
10939 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10940 if (parallelPorts[slot])
10941 that->onParallelPortChange(parallelPorts[slot]);
10942
10943 if (flModifications & IsModified_Storage)
10944 that->onStorageControllerChange();
10945
10946#if 0
10947 if (flModifications & IsModified_BandwidthControl)
10948 that->onBandwidthControlChange();
10949#endif
10950 }
10951}
10952
10953/**
10954 * Commits all the changes to machine settings.
10955 *
10956 * Note that this operation is supposed to never fail.
10957 *
10958 * @note Locks this object and children for writing.
10959 */
10960void Machine::commit()
10961{
10962 AutoCaller autoCaller(this);
10963 AssertComRCReturnVoid(autoCaller.rc());
10964
10965 AutoCaller peerCaller(mPeer);
10966 AssertComRCReturnVoid(peerCaller.rc());
10967
10968 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10969
10970 /*
10971 * use safe commit to ensure Snapshot machines (that share mUserData)
10972 * will still refer to a valid memory location
10973 */
10974 mUserData.commitCopy();
10975
10976 mHWData.commit();
10977
10978 if (mMediaData.isBackedUp())
10979 commitMedia();
10980
10981 mBIOSSettings->commit();
10982 mVRDEServer->commit();
10983 mAudioAdapter->commit();
10984 mUSBController->commit();
10985 mBandwidthControl->commit();
10986
10987 /* Keep the original network adapter count until this point, so that
10988 * discarding a chipset type change will not lose settings. */
10989 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10990 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10991 mNetworkAdapters[slot]->commit();
10992 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10993 mSerialPorts[slot]->commit();
10994 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10995 mParallelPorts[slot]->commit();
10996
10997 bool commitStorageControllers = false;
10998
10999 if (mStorageControllers.isBackedUp())
11000 {
11001 mStorageControllers.commit();
11002
11003 if (mPeer)
11004 {
11005 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
11006
11007 /* Commit all changes to new controllers (this will reshare data with
11008 * peers for those who have peers) */
11009 StorageControllerList *newList = new StorageControllerList();
11010 StorageControllerList::const_iterator it = mStorageControllers->begin();
11011 while (it != mStorageControllers->end())
11012 {
11013 (*it)->commit();
11014
11015 /* look if this controller has a peer device */
11016 ComObjPtr<StorageController> peer = (*it)->getPeer();
11017 if (!peer)
11018 {
11019 /* no peer means the device is a newly created one;
11020 * create a peer owning data this device share it with */
11021 peer.createObject();
11022 peer->init(mPeer, *it, true /* aReshare */);
11023 }
11024 else
11025 {
11026 /* remove peer from the old list */
11027 mPeer->mStorageControllers->remove(peer);
11028 }
11029 /* and add it to the new list */
11030 newList->push_back(peer);
11031
11032 ++it;
11033 }
11034
11035 /* uninit old peer's controllers that are left */
11036 it = mPeer->mStorageControllers->begin();
11037 while (it != mPeer->mStorageControllers->end())
11038 {
11039 (*it)->uninit();
11040 ++it;
11041 }
11042
11043 /* attach new list of controllers to our peer */
11044 mPeer->mStorageControllers.attach(newList);
11045 }
11046 else
11047 {
11048 /* we have no peer (our parent is the newly created machine);
11049 * just commit changes to devices */
11050 commitStorageControllers = true;
11051 }
11052 }
11053 else
11054 {
11055 /* the list of controllers itself is not changed,
11056 * just commit changes to controllers themselves */
11057 commitStorageControllers = true;
11058 }
11059
11060 if (commitStorageControllers)
11061 {
11062 StorageControllerList::const_iterator it = mStorageControllers->begin();
11063 while (it != mStorageControllers->end())
11064 {
11065 (*it)->commit();
11066 ++it;
11067 }
11068 }
11069
11070 if (isSessionMachine())
11071 {
11072 /* attach new data to the primary machine and reshare it */
11073 mPeer->mUserData.attach(mUserData);
11074 mPeer->mHWData.attach(mHWData);
11075 /* mMediaData is reshared by fixupMedia */
11076 // mPeer->mMediaData.attach(mMediaData);
11077 Assert(mPeer->mMediaData.data() == mMediaData.data());
11078 }
11079}
11080
11081/**
11082 * Copies all the hardware data from the given machine.
11083 *
11084 * Currently, only called when the VM is being restored from a snapshot. In
11085 * particular, this implies that the VM is not running during this method's
11086 * call.
11087 *
11088 * @note This method must be called from under this object's lock.
11089 *
11090 * @note This method doesn't call #commit(), so all data remains backed up and
11091 * unsaved.
11092 */
11093void Machine::copyFrom(Machine *aThat)
11094{
11095 AssertReturnVoid(!isSnapshotMachine());
11096 AssertReturnVoid(aThat->isSnapshotMachine());
11097
11098 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11099
11100 mHWData.assignCopy(aThat->mHWData);
11101
11102 // create copies of all shared folders (mHWData after attaching a copy
11103 // contains just references to original objects)
11104 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11105 it != mHWData->mSharedFolders.end();
11106 ++it)
11107 {
11108 ComObjPtr<SharedFolder> folder;
11109 folder.createObject();
11110 HRESULT rc = folder->initCopy(getMachine(), *it);
11111 AssertComRC(rc);
11112 *it = folder;
11113 }
11114
11115 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11116 mVRDEServer->copyFrom(aThat->mVRDEServer);
11117 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11118 mUSBController->copyFrom(aThat->mUSBController);
11119 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11120
11121 /* create private copies of all controllers */
11122 mStorageControllers.backup();
11123 mStorageControllers->clear();
11124 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11125 it != aThat->mStorageControllers->end();
11126 ++it)
11127 {
11128 ComObjPtr<StorageController> ctrl;
11129 ctrl.createObject();
11130 ctrl->initCopy(this, *it);
11131 mStorageControllers->push_back(ctrl);
11132 }
11133
11134 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11135 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11136 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11137 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11138 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11139 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11140}
11141
11142/**
11143 * Returns whether the given storage controller is hotplug capable.
11144 *
11145 * @returns true if the controller supports hotplugging
11146 * false otherwise.
11147 * @param enmCtrlType The controller type to check for.
11148 */
11149bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11150{
11151 switch (enmCtrlType)
11152 {
11153 case StorageControllerType_IntelAhci:
11154 return true;
11155 case StorageControllerType_LsiLogic:
11156 case StorageControllerType_LsiLogicSas:
11157 case StorageControllerType_BusLogic:
11158 case StorageControllerType_PIIX3:
11159 case StorageControllerType_PIIX4:
11160 case StorageControllerType_ICH6:
11161 case StorageControllerType_I82078:
11162 default:
11163 return false;
11164 }
11165}
11166
11167#ifdef VBOX_WITH_RESOURCE_USAGE_API
11168
11169void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11170{
11171 AssertReturnVoid(isWriteLockOnCurrentThread());
11172 AssertPtrReturnVoid(aCollector);
11173
11174 pm::CollectorHAL *hal = aCollector->getHAL();
11175 /* Create sub metrics */
11176 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11177 "Percentage of processor time spent in user mode by the VM process.");
11178 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11179 "Percentage of processor time spent in kernel mode by the VM process.");
11180 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11181 "Size of resident portion of VM process in memory.");
11182 /* Create and register base metrics */
11183 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11184 cpuLoadUser, cpuLoadKernel);
11185 aCollector->registerBaseMetric(cpuLoad);
11186 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11187 ramUsageUsed);
11188 aCollector->registerBaseMetric(ramUsage);
11189
11190 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11191 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11192 new pm::AggregateAvg()));
11193 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11194 new pm::AggregateMin()));
11195 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11196 new pm::AggregateMax()));
11197 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11198 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11199 new pm::AggregateAvg()));
11200 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11201 new pm::AggregateMin()));
11202 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11203 new pm::AggregateMax()));
11204
11205 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11206 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11207 new pm::AggregateAvg()));
11208 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11209 new pm::AggregateMin()));
11210 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11211 new pm::AggregateMax()));
11212
11213
11214 /* Guest metrics collector */
11215 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11216 aCollector->registerGuest(mCollectorGuest);
11217 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11218 this, __PRETTY_FUNCTION__, mCollectorGuest));
11219
11220 /* Create sub metrics */
11221 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11222 "Percentage of processor time spent in user mode as seen by the guest.");
11223 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11224 "Percentage of processor time spent in kernel mode as seen by the guest.");
11225 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11226 "Percentage of processor time spent idling as seen by the guest.");
11227
11228 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11229 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11230 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11231 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11232 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11233 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11234
11235 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11236
11237 /* Create and register base metrics */
11238 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11239 guestLoadUser, guestLoadKernel, guestLoadIdle);
11240 aCollector->registerBaseMetric(guestCpuLoad);
11241
11242 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11243 guestMemTotal, guestMemFree,
11244 guestMemBalloon, guestMemShared,
11245 guestMemCache, guestPagedTotal);
11246 aCollector->registerBaseMetric(guestCpuMem);
11247
11248 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11252
11253 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11254 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11255 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11257
11258 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11259 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11260 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11261 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11262
11263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11267
11268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11272
11273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11277
11278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11282
11283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11284 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11287
11288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11289 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11292}
11293
11294void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11295{
11296 AssertReturnVoid(isWriteLockOnCurrentThread());
11297
11298 if (aCollector)
11299 {
11300 aCollector->unregisterMetricsFor(aMachine);
11301 aCollector->unregisterBaseMetricsFor(aMachine);
11302 }
11303}
11304
11305#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11306
11307
11308////////////////////////////////////////////////////////////////////////////////
11309
11310DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11311
11312HRESULT SessionMachine::FinalConstruct()
11313{
11314 LogFlowThisFunc(("\n"));
11315
11316#if defined(RT_OS_WINDOWS)
11317 mIPCSem = NULL;
11318#elif defined(RT_OS_OS2)
11319 mIPCSem = NULLHANDLE;
11320#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11321 mIPCSem = -1;
11322#else
11323# error "Port me!"
11324#endif
11325
11326 return BaseFinalConstruct();
11327}
11328
11329void SessionMachine::FinalRelease()
11330{
11331 LogFlowThisFunc(("\n"));
11332
11333 uninit(Uninit::Unexpected);
11334
11335 BaseFinalRelease();
11336}
11337
11338/**
11339 * @note Must be called only by Machine::openSession() from its own write lock.
11340 */
11341HRESULT SessionMachine::init(Machine *aMachine)
11342{
11343 LogFlowThisFuncEnter();
11344 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11345
11346 AssertReturn(aMachine, E_INVALIDARG);
11347
11348 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11349
11350 /* Enclose the state transition NotReady->InInit->Ready */
11351 AutoInitSpan autoInitSpan(this);
11352 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11353
11354 /* create the interprocess semaphore */
11355#if defined(RT_OS_WINDOWS)
11356 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11357 for (size_t i = 0; i < mIPCSemName.length(); i++)
11358 if (mIPCSemName.raw()[i] == '\\')
11359 mIPCSemName.raw()[i] = '/';
11360 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11361 ComAssertMsgRet(mIPCSem,
11362 ("Cannot create IPC mutex '%ls', err=%d",
11363 mIPCSemName.raw(), ::GetLastError()),
11364 E_FAIL);
11365#elif defined(RT_OS_OS2)
11366 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11367 aMachine->mData->mUuid.raw());
11368 mIPCSemName = ipcSem;
11369 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11370 ComAssertMsgRet(arc == NO_ERROR,
11371 ("Cannot create IPC mutex '%s', arc=%ld",
11372 ipcSem.c_str(), arc),
11373 E_FAIL);
11374#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11375# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11376# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11377 /** @todo Check that this still works correctly. */
11378 AssertCompileSize(key_t, 8);
11379# else
11380 AssertCompileSize(key_t, 4);
11381# endif
11382 key_t key;
11383 mIPCSem = -1;
11384 mIPCKey = "0";
11385 for (uint32_t i = 0; i < 1 << 24; i++)
11386 {
11387 key = ((uint32_t)'V' << 24) | i;
11388 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11389 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11390 {
11391 mIPCSem = sem;
11392 if (sem >= 0)
11393 mIPCKey = BstrFmt("%u", key);
11394 break;
11395 }
11396 }
11397# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11398 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11399 char *pszSemName = NULL;
11400 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11401 key_t key = ::ftok(pszSemName, 'V');
11402 RTStrFree(pszSemName);
11403
11404 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11405# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11406
11407 int errnoSave = errno;
11408 if (mIPCSem < 0 && errnoSave == ENOSYS)
11409 {
11410 setError(E_FAIL,
11411 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11412 "support for SysV IPC. Check the host kernel configuration for "
11413 "CONFIG_SYSVIPC=y"));
11414 return E_FAIL;
11415 }
11416 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11417 * the IPC semaphores */
11418 if (mIPCSem < 0 && errnoSave == ENOSPC)
11419 {
11420#ifdef RT_OS_LINUX
11421 setError(E_FAIL,
11422 tr("Cannot create IPC semaphore because the system limit for the "
11423 "maximum number of semaphore sets (SEMMNI), or the system wide "
11424 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11425 "current set of SysV IPC semaphores can be determined from "
11426 "the file /proc/sysvipc/sem"));
11427#else
11428 setError(E_FAIL,
11429 tr("Cannot create IPC semaphore because the system-imposed limit "
11430 "on the maximum number of allowed semaphores or semaphore "
11431 "identifiers system-wide would be exceeded"));
11432#endif
11433 return E_FAIL;
11434 }
11435 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11436 E_FAIL);
11437 /* set the initial value to 1 */
11438 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11439 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11440 E_FAIL);
11441#else
11442# error "Port me!"
11443#endif
11444
11445 /* memorize the peer Machine */
11446 unconst(mPeer) = aMachine;
11447 /* share the parent pointer */
11448 unconst(mParent) = aMachine->mParent;
11449
11450 /* take the pointers to data to share */
11451 mData.share(aMachine->mData);
11452 mSSData.share(aMachine->mSSData);
11453
11454 mUserData.share(aMachine->mUserData);
11455 mHWData.share(aMachine->mHWData);
11456 mMediaData.share(aMachine->mMediaData);
11457
11458 mStorageControllers.allocate();
11459 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11460 it != aMachine->mStorageControllers->end();
11461 ++it)
11462 {
11463 ComObjPtr<StorageController> ctl;
11464 ctl.createObject();
11465 ctl->init(this, *it);
11466 mStorageControllers->push_back(ctl);
11467 }
11468
11469 unconst(mBIOSSettings).createObject();
11470 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11471 /* create another VRDEServer object that will be mutable */
11472 unconst(mVRDEServer).createObject();
11473 mVRDEServer->init(this, aMachine->mVRDEServer);
11474 /* create another audio adapter object that will be mutable */
11475 unconst(mAudioAdapter).createObject();
11476 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11477 /* create a list of serial ports that will be mutable */
11478 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11479 {
11480 unconst(mSerialPorts[slot]).createObject();
11481 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11482 }
11483 /* create a list of parallel ports that will be mutable */
11484 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11485 {
11486 unconst(mParallelPorts[slot]).createObject();
11487 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11488 }
11489 /* create another USB controller object that will be mutable */
11490 unconst(mUSBController).createObject();
11491 mUSBController->init(this, aMachine->mUSBController);
11492
11493 /* create a list of network adapters that will be mutable */
11494 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11495 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11496 {
11497 unconst(mNetworkAdapters[slot]).createObject();
11498 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11499 }
11500
11501 /* create another bandwidth control object that will be mutable */
11502 unconst(mBandwidthControl).createObject();
11503 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11504
11505 /* default is to delete saved state on Saved -> PoweredOff transition */
11506 mRemoveSavedState = true;
11507
11508 /* Confirm a successful initialization when it's the case */
11509 autoInitSpan.setSucceeded();
11510
11511 LogFlowThisFuncLeave();
11512 return S_OK;
11513}
11514
11515/**
11516 * Uninitializes this session object. If the reason is other than
11517 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11518 *
11519 * @param aReason uninitialization reason
11520 *
11521 * @note Locks mParent + this object for writing.
11522 */
11523void SessionMachine::uninit(Uninit::Reason aReason)
11524{
11525 LogFlowThisFuncEnter();
11526 LogFlowThisFunc(("reason=%d\n", aReason));
11527
11528 /*
11529 * Strongly reference ourselves to prevent this object deletion after
11530 * mData->mSession.mMachine.setNull() below (which can release the last
11531 * reference and call the destructor). Important: this must be done before
11532 * accessing any members (and before AutoUninitSpan that does it as well).
11533 * This self reference will be released as the very last step on return.
11534 */
11535 ComObjPtr<SessionMachine> selfRef = this;
11536
11537 /* Enclose the state transition Ready->InUninit->NotReady */
11538 AutoUninitSpan autoUninitSpan(this);
11539 if (autoUninitSpan.uninitDone())
11540 {
11541 LogFlowThisFunc(("Already uninitialized\n"));
11542 LogFlowThisFuncLeave();
11543 return;
11544 }
11545
11546 if (autoUninitSpan.initFailed())
11547 {
11548 /* We've been called by init() because it's failed. It's not really
11549 * necessary (nor it's safe) to perform the regular uninit sequence
11550 * below, the following is enough.
11551 */
11552 LogFlowThisFunc(("Initialization failed.\n"));
11553#if defined(RT_OS_WINDOWS)
11554 if (mIPCSem)
11555 ::CloseHandle(mIPCSem);
11556 mIPCSem = NULL;
11557#elif defined(RT_OS_OS2)
11558 if (mIPCSem != NULLHANDLE)
11559 ::DosCloseMutexSem(mIPCSem);
11560 mIPCSem = NULLHANDLE;
11561#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11562 if (mIPCSem >= 0)
11563 ::semctl(mIPCSem, 0, IPC_RMID);
11564 mIPCSem = -1;
11565# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11566 mIPCKey = "0";
11567# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11568#else
11569# error "Port me!"
11570#endif
11571 uninitDataAndChildObjects();
11572 mData.free();
11573 unconst(mParent) = NULL;
11574 unconst(mPeer) = NULL;
11575 LogFlowThisFuncLeave();
11576 return;
11577 }
11578
11579 MachineState_T lastState;
11580 {
11581 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11582 lastState = mData->mMachineState;
11583 }
11584 NOREF(lastState);
11585
11586#ifdef VBOX_WITH_USB
11587 // release all captured USB devices, but do this before requesting the locks below
11588 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11589 {
11590 /* Console::captureUSBDevices() is called in the VM process only after
11591 * setting the machine state to Starting or Restoring.
11592 * Console::detachAllUSBDevices() will be called upon successful
11593 * termination. So, we need to release USB devices only if there was
11594 * an abnormal termination of a running VM.
11595 *
11596 * This is identical to SessionMachine::DetachAllUSBDevices except
11597 * for the aAbnormal argument. */
11598 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11599 AssertComRC(rc);
11600 NOREF(rc);
11601
11602 USBProxyService *service = mParent->host()->usbProxyService();
11603 if (service)
11604 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11605 }
11606#endif /* VBOX_WITH_USB */
11607
11608 // we need to lock this object in uninit() because the lock is shared
11609 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11610 // and others need mParent lock, and USB needs host lock.
11611 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11612
11613#if 0
11614 // Trigger async cleanup tasks, avoid doing things here which are not
11615 // vital to be done immediately and maybe need more locks. This calls
11616 // Machine::unregisterMetrics().
11617 mParent->onMachineUninit(mPeer);
11618#else
11619 /*
11620 * It is safe to call Machine::unregisterMetrics() here because
11621 * PerformanceCollector::samplerCallback no longer accesses guest methods
11622 * holding the lock.
11623 */
11624 unregisterMetrics(mParent->performanceCollector(), mPeer);
11625#endif
11626 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11627 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11628 this, __PRETTY_FUNCTION__, mCollectorGuest));
11629 if (mCollectorGuest)
11630 {
11631 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11632 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11633 mCollectorGuest = NULL;
11634 }
11635
11636 if (aReason == Uninit::Abnormal)
11637 {
11638 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11639 Global::IsOnlineOrTransient(lastState)));
11640
11641 /* reset the state to Aborted */
11642 if (mData->mMachineState != MachineState_Aborted)
11643 setMachineState(MachineState_Aborted);
11644 }
11645
11646 // any machine settings modified?
11647 if (mData->flModifications)
11648 {
11649 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11650 rollback(false /* aNotify */);
11651 }
11652
11653 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11654 || !mConsoleTaskData.mSnapshot);
11655 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11656 {
11657 LogWarningThisFunc(("canceling failed save state request!\n"));
11658 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11659 }
11660 else if (!mConsoleTaskData.mSnapshot.isNull())
11661 {
11662 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11663
11664 /* delete all differencing hard disks created (this will also attach
11665 * their parents back by rolling back mMediaData) */
11666 rollbackMedia();
11667
11668 // delete the saved state file (it might have been already created)
11669 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11670 // think it's still in use
11671 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11672 mConsoleTaskData.mSnapshot->uninit();
11673 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11674 }
11675
11676 if (!mData->mSession.mType.isEmpty())
11677 {
11678 /* mType is not null when this machine's process has been started by
11679 * Machine::LaunchVMProcess(), therefore it is our child. We
11680 * need to queue the PID to reap the process (and avoid zombies on
11681 * Linux). */
11682 Assert(mData->mSession.mPid != NIL_RTPROCESS);
11683 mParent->addProcessToReap(mData->mSession.mPid);
11684 }
11685
11686 mData->mSession.mPid = NIL_RTPROCESS;
11687
11688 if (aReason == Uninit::Unexpected)
11689 {
11690 /* Uninitialization didn't come from #checkForDeath(), so tell the
11691 * client watcher thread to update the set of machines that have open
11692 * sessions. */
11693 mParent->updateClientWatcher();
11694 }
11695
11696 /* uninitialize all remote controls */
11697 if (mData->mSession.mRemoteControls.size())
11698 {
11699 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11700 mData->mSession.mRemoteControls.size()));
11701
11702 Data::Session::RemoteControlList::iterator it =
11703 mData->mSession.mRemoteControls.begin();
11704 while (it != mData->mSession.mRemoteControls.end())
11705 {
11706 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11707 HRESULT rc = (*it)->Uninitialize();
11708 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11709 if (FAILED(rc))
11710 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11711 ++it;
11712 }
11713 mData->mSession.mRemoteControls.clear();
11714 }
11715
11716 /*
11717 * An expected uninitialization can come only from #checkForDeath().
11718 * Otherwise it means that something's gone really wrong (for example,
11719 * the Session implementation has released the VirtualBox reference
11720 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11721 * etc). However, it's also possible, that the client releases the IPC
11722 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11723 * but the VirtualBox release event comes first to the server process.
11724 * This case is practically possible, so we should not assert on an
11725 * unexpected uninit, just log a warning.
11726 */
11727
11728 if ((aReason == Uninit::Unexpected))
11729 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11730
11731 if (aReason != Uninit::Normal)
11732 {
11733 mData->mSession.mDirectControl.setNull();
11734 }
11735 else
11736 {
11737 /* this must be null here (see #OnSessionEnd()) */
11738 Assert(mData->mSession.mDirectControl.isNull());
11739 Assert(mData->mSession.mState == SessionState_Unlocking);
11740 Assert(!mData->mSession.mProgress.isNull());
11741 }
11742 if (mData->mSession.mProgress)
11743 {
11744 if (aReason == Uninit::Normal)
11745 mData->mSession.mProgress->notifyComplete(S_OK);
11746 else
11747 mData->mSession.mProgress->notifyComplete(E_FAIL,
11748 COM_IIDOF(ISession),
11749 getComponentName(),
11750 tr("The VM session was aborted"));
11751 mData->mSession.mProgress.setNull();
11752 }
11753
11754 /* remove the association between the peer machine and this session machine */
11755 Assert( (SessionMachine*)mData->mSession.mMachine == this
11756 || aReason == Uninit::Unexpected);
11757
11758 /* reset the rest of session data */
11759 mData->mSession.mMachine.setNull();
11760 mData->mSession.mState = SessionState_Unlocked;
11761 mData->mSession.mType.setNull();
11762
11763 /* close the interprocess semaphore before leaving the exclusive lock */
11764#if defined(RT_OS_WINDOWS)
11765 if (mIPCSem)
11766 ::CloseHandle(mIPCSem);
11767 mIPCSem = NULL;
11768#elif defined(RT_OS_OS2)
11769 if (mIPCSem != NULLHANDLE)
11770 ::DosCloseMutexSem(mIPCSem);
11771 mIPCSem = NULLHANDLE;
11772#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11773 if (mIPCSem >= 0)
11774 ::semctl(mIPCSem, 0, IPC_RMID);
11775 mIPCSem = -1;
11776# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11777 mIPCKey = "0";
11778# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11779#else
11780# error "Port me!"
11781#endif
11782
11783 /* fire an event */
11784 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11785
11786 uninitDataAndChildObjects();
11787
11788 /* free the essential data structure last */
11789 mData.free();
11790
11791 /* release the exclusive lock before setting the below two to NULL */
11792 multilock.release();
11793
11794 unconst(mParent) = NULL;
11795 unconst(mPeer) = NULL;
11796
11797 LogFlowThisFuncLeave();
11798}
11799
11800// util::Lockable interface
11801////////////////////////////////////////////////////////////////////////////////
11802
11803/**
11804 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11805 * with the primary Machine instance (mPeer).
11806 */
11807RWLockHandle *SessionMachine::lockHandle() const
11808{
11809 AssertReturn(mPeer != NULL, NULL);
11810 return mPeer->lockHandle();
11811}
11812
11813// IInternalMachineControl methods
11814////////////////////////////////////////////////////////////////////////////////
11815
11816/**
11817 * Passes collected guest statistics to performance collector object
11818 */
11819STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11820 ULONG aCpuKernel, ULONG aCpuIdle,
11821 ULONG aMemTotal, ULONG aMemFree,
11822 ULONG aMemBalloon, ULONG aMemShared,
11823 ULONG aMemCache, ULONG aPageTotal,
11824 ULONG aAllocVMM, ULONG aFreeVMM,
11825 ULONG aBalloonedVMM, ULONG aSharedVMM)
11826{
11827 if (mCollectorGuest)
11828 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11829 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11830 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11831 aBalloonedVMM, aSharedVMM);
11832
11833 return S_OK;
11834}
11835
11836/**
11837 * @note Locks this object for writing.
11838 */
11839STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11840{
11841 AutoCaller autoCaller(this);
11842 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11843
11844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11845
11846 mRemoveSavedState = aRemove;
11847
11848 return S_OK;
11849}
11850
11851/**
11852 * @note Locks the same as #setMachineState() does.
11853 */
11854STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11855{
11856 return setMachineState(aMachineState);
11857}
11858
11859/**
11860 * @note Locks this object for reading.
11861 */
11862STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11863{
11864 AutoCaller autoCaller(this);
11865 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11866
11867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11868
11869#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11870 mIPCSemName.cloneTo(aId);
11871 return S_OK;
11872#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11873# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11874 mIPCKey.cloneTo(aId);
11875# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11876 mData->m_strConfigFileFull.cloneTo(aId);
11877# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11878 return S_OK;
11879#else
11880# error "Port me!"
11881#endif
11882}
11883
11884/**
11885 * @note Locks this object for writing.
11886 */
11887STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11888{
11889 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11890 AutoCaller autoCaller(this);
11891 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11892
11893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11894
11895 if (mData->mSession.mState != SessionState_Locked)
11896 return VBOX_E_INVALID_OBJECT_STATE;
11897
11898 if (!mData->mSession.mProgress.isNull())
11899 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11900
11901 LogFlowThisFunc(("returns S_OK.\n"));
11902 return S_OK;
11903}
11904
11905/**
11906 * @note Locks this object for writing.
11907 */
11908STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11909{
11910 AutoCaller autoCaller(this);
11911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11912
11913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11914
11915 if (mData->mSession.mState != SessionState_Locked)
11916 return VBOX_E_INVALID_OBJECT_STATE;
11917
11918 /* Finalize the LaunchVMProcess progress object. */
11919 if (mData->mSession.mProgress)
11920 {
11921 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11922 mData->mSession.mProgress.setNull();
11923 }
11924
11925 if (SUCCEEDED((HRESULT)iResult))
11926 {
11927#ifdef VBOX_WITH_RESOURCE_USAGE_API
11928 /* The VM has been powered up successfully, so it makes sense
11929 * now to offer the performance metrics for a running machine
11930 * object. Doing it earlier wouldn't be safe. */
11931 registerMetrics(mParent->performanceCollector(), mPeer,
11932 mData->mSession.mPid);
11933#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11934 }
11935
11936 return S_OK;
11937}
11938
11939/**
11940 * @note Locks this object for writing.
11941 */
11942STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11943{
11944 LogFlowThisFuncEnter();
11945
11946 CheckComArgOutPointerValid(aProgress);
11947
11948 AutoCaller autoCaller(this);
11949 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11950
11951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11952
11953 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11954 E_FAIL);
11955
11956 /* create a progress object to track operation completion */
11957 ComObjPtr<Progress> pProgress;
11958 pProgress.createObject();
11959 pProgress->init(getVirtualBox(),
11960 static_cast<IMachine *>(this) /* aInitiator */,
11961 Bstr(tr("Stopping the virtual machine")).raw(),
11962 FALSE /* aCancelable */);
11963
11964 /* fill in the console task data */
11965 mConsoleTaskData.mLastState = mData->mMachineState;
11966 mConsoleTaskData.mProgress = pProgress;
11967
11968 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11969 setMachineState(MachineState_Stopping);
11970
11971 pProgress.queryInterfaceTo(aProgress);
11972
11973 return S_OK;
11974}
11975
11976/**
11977 * @note Locks this object for writing.
11978 */
11979STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
11980{
11981 LogFlowThisFuncEnter();
11982
11983 AutoCaller autoCaller(this);
11984 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11985
11986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11987
11988 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
11989 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
11990 && mConsoleTaskData.mLastState != MachineState_Null,
11991 E_FAIL);
11992
11993 /*
11994 * On failure, set the state to the state we had when BeginPoweringDown()
11995 * was called (this is expected by Console::PowerDown() and the associated
11996 * task). On success the VM process already changed the state to
11997 * MachineState_PoweredOff, so no need to do anything.
11998 */
11999 if (FAILED(iResult))
12000 setMachineState(mConsoleTaskData.mLastState);
12001
12002 /* notify the progress object about operation completion */
12003 Assert(mConsoleTaskData.mProgress);
12004 if (SUCCEEDED(iResult))
12005 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12006 else
12007 {
12008 Utf8Str strErrMsg(aErrMsg);
12009 if (strErrMsg.length())
12010 mConsoleTaskData.mProgress->notifyComplete(iResult,
12011 COM_IIDOF(ISession),
12012 getComponentName(),
12013 strErrMsg.c_str());
12014 else
12015 mConsoleTaskData.mProgress->notifyComplete(iResult);
12016 }
12017
12018 /* clear out the temporary saved state data */
12019 mConsoleTaskData.mLastState = MachineState_Null;
12020 mConsoleTaskData.mProgress.setNull();
12021
12022 LogFlowThisFuncLeave();
12023 return S_OK;
12024}
12025
12026
12027/**
12028 * Goes through the USB filters of the given machine to see if the given
12029 * device matches any filter or not.
12030 *
12031 * @note Locks the same as USBController::hasMatchingFilter() does.
12032 */
12033STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12034 BOOL *aMatched,
12035 ULONG *aMaskedIfs)
12036{
12037 LogFlowThisFunc(("\n"));
12038
12039 CheckComArgNotNull(aUSBDevice);
12040 CheckComArgOutPointerValid(aMatched);
12041
12042 AutoCaller autoCaller(this);
12043 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12044
12045#ifdef VBOX_WITH_USB
12046 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12047#else
12048 NOREF(aUSBDevice);
12049 NOREF(aMaskedIfs);
12050 *aMatched = FALSE;
12051#endif
12052
12053 return S_OK;
12054}
12055
12056/**
12057 * @note Locks the same as Host::captureUSBDevice() does.
12058 */
12059STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12060{
12061 LogFlowThisFunc(("\n"));
12062
12063 AutoCaller autoCaller(this);
12064 AssertComRCReturnRC(autoCaller.rc());
12065
12066#ifdef VBOX_WITH_USB
12067 /* if captureDeviceForVM() fails, it must have set extended error info */
12068 clearError();
12069 MultiResult rc = mParent->host()->checkUSBProxyService();
12070 if (FAILED(rc)) return rc;
12071
12072 USBProxyService *service = mParent->host()->usbProxyService();
12073 AssertReturn(service, E_FAIL);
12074 return service->captureDeviceForVM(this, Guid(aId).ref());
12075#else
12076 NOREF(aId);
12077 return E_NOTIMPL;
12078#endif
12079}
12080
12081/**
12082 * @note Locks the same as Host::detachUSBDevice() does.
12083 */
12084STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12085{
12086 LogFlowThisFunc(("\n"));
12087
12088 AutoCaller autoCaller(this);
12089 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12090
12091#ifdef VBOX_WITH_USB
12092 USBProxyService *service = mParent->host()->usbProxyService();
12093 AssertReturn(service, E_FAIL);
12094 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12095#else
12096 NOREF(aId);
12097 NOREF(aDone);
12098 return E_NOTIMPL;
12099#endif
12100}
12101
12102/**
12103 * Inserts all machine filters to the USB proxy service and then calls
12104 * Host::autoCaptureUSBDevices().
12105 *
12106 * Called by Console from the VM process upon VM startup.
12107 *
12108 * @note Locks what called methods lock.
12109 */
12110STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12111{
12112 LogFlowThisFunc(("\n"));
12113
12114 AutoCaller autoCaller(this);
12115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12116
12117#ifdef VBOX_WITH_USB
12118 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12119 AssertComRC(rc);
12120 NOREF(rc);
12121
12122 USBProxyService *service = mParent->host()->usbProxyService();
12123 AssertReturn(service, E_FAIL);
12124 return service->autoCaptureDevicesForVM(this);
12125#else
12126 return S_OK;
12127#endif
12128}
12129
12130/**
12131 * Removes all machine filters from the USB proxy service and then calls
12132 * Host::detachAllUSBDevices().
12133 *
12134 * Called by Console from the VM process upon normal VM termination or by
12135 * SessionMachine::uninit() upon abnormal VM termination (from under the
12136 * Machine/SessionMachine lock).
12137 *
12138 * @note Locks what called methods lock.
12139 */
12140STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12141{
12142 LogFlowThisFunc(("\n"));
12143
12144 AutoCaller autoCaller(this);
12145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12146
12147#ifdef VBOX_WITH_USB
12148 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12149 AssertComRC(rc);
12150 NOREF(rc);
12151
12152 USBProxyService *service = mParent->host()->usbProxyService();
12153 AssertReturn(service, E_FAIL);
12154 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12155#else
12156 NOREF(aDone);
12157 return S_OK;
12158#endif
12159}
12160
12161/**
12162 * @note Locks this object for writing.
12163 */
12164STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12165 IProgress **aProgress)
12166{
12167 LogFlowThisFuncEnter();
12168
12169 AssertReturn(aSession, E_INVALIDARG);
12170 AssertReturn(aProgress, E_INVALIDARG);
12171
12172 AutoCaller autoCaller(this);
12173
12174 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12175 /*
12176 * We don't assert below because it might happen that a non-direct session
12177 * informs us it is closed right after we've been uninitialized -- it's ok.
12178 */
12179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12180
12181 /* get IInternalSessionControl interface */
12182 ComPtr<IInternalSessionControl> control(aSession);
12183
12184 ComAssertRet(!control.isNull(), E_INVALIDARG);
12185
12186 /* Creating a Progress object requires the VirtualBox lock, and
12187 * thus locking it here is required by the lock order rules. */
12188 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12189
12190 if (control == mData->mSession.mDirectControl)
12191 {
12192 ComAssertRet(aProgress, E_POINTER);
12193
12194 /* The direct session is being normally closed by the client process
12195 * ----------------------------------------------------------------- */
12196
12197 /* go to the closing state (essential for all open*Session() calls and
12198 * for #checkForDeath()) */
12199 Assert(mData->mSession.mState == SessionState_Locked);
12200 mData->mSession.mState = SessionState_Unlocking;
12201
12202 /* set direct control to NULL to release the remote instance */
12203 mData->mSession.mDirectControl.setNull();
12204 LogFlowThisFunc(("Direct control is set to NULL\n"));
12205
12206 if (mData->mSession.mProgress)
12207 {
12208 /* finalize the progress, someone might wait if a frontend
12209 * closes the session before powering on the VM. */
12210 mData->mSession.mProgress->notifyComplete(E_FAIL,
12211 COM_IIDOF(ISession),
12212 getComponentName(),
12213 tr("The VM session was closed before any attempt to power it on"));
12214 mData->mSession.mProgress.setNull();
12215 }
12216
12217 /* Create the progress object the client will use to wait until
12218 * #checkForDeath() is called to uninitialize this session object after
12219 * it releases the IPC semaphore.
12220 * Note! Because we're "reusing" mProgress here, this must be a proxy
12221 * object just like for LaunchVMProcess. */
12222 Assert(mData->mSession.mProgress.isNull());
12223 ComObjPtr<ProgressProxy> progress;
12224 progress.createObject();
12225 ComPtr<IUnknown> pPeer(mPeer);
12226 progress->init(mParent, pPeer,
12227 Bstr(tr("Closing session")).raw(),
12228 FALSE /* aCancelable */);
12229 progress.queryInterfaceTo(aProgress);
12230 mData->mSession.mProgress = progress;
12231 }
12232 else
12233 {
12234 /* the remote session is being normally closed */
12235 Data::Session::RemoteControlList::iterator it =
12236 mData->mSession.mRemoteControls.begin();
12237 while (it != mData->mSession.mRemoteControls.end())
12238 {
12239 if (control == *it)
12240 break;
12241 ++it;
12242 }
12243 BOOL found = it != mData->mSession.mRemoteControls.end();
12244 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12245 E_INVALIDARG);
12246 // This MUST be erase(it), not remove(*it) as the latter triggers a
12247 // very nasty use after free due to the place where the value "lives".
12248 mData->mSession.mRemoteControls.erase(it);
12249 }
12250
12251 /* signal the client watcher thread, because the client is going away */
12252 mParent->updateClientWatcher();
12253
12254 LogFlowThisFuncLeave();
12255 return S_OK;
12256}
12257
12258/**
12259 * @note Locks this object for writing.
12260 */
12261STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12262{
12263 LogFlowThisFuncEnter();
12264
12265 CheckComArgOutPointerValid(aProgress);
12266 CheckComArgOutPointerValid(aStateFilePath);
12267
12268 AutoCaller autoCaller(this);
12269 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12270
12271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12272
12273 AssertReturn( mData->mMachineState == MachineState_Paused
12274 && mConsoleTaskData.mLastState == MachineState_Null
12275 && mConsoleTaskData.strStateFilePath.isEmpty(),
12276 E_FAIL);
12277
12278 /* create a progress object to track operation completion */
12279 ComObjPtr<Progress> pProgress;
12280 pProgress.createObject();
12281 pProgress->init(getVirtualBox(),
12282 static_cast<IMachine *>(this) /* aInitiator */,
12283 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12284 FALSE /* aCancelable */);
12285
12286 Utf8Str strStateFilePath;
12287 /* stateFilePath is null when the machine is not running */
12288 if (mData->mMachineState == MachineState_Paused)
12289 composeSavedStateFilename(strStateFilePath);
12290
12291 /* fill in the console task data */
12292 mConsoleTaskData.mLastState = mData->mMachineState;
12293 mConsoleTaskData.strStateFilePath = strStateFilePath;
12294 mConsoleTaskData.mProgress = pProgress;
12295
12296 /* set the state to Saving (this is expected by Console::SaveState()) */
12297 setMachineState(MachineState_Saving);
12298
12299 strStateFilePath.cloneTo(aStateFilePath);
12300 pProgress.queryInterfaceTo(aProgress);
12301
12302 return S_OK;
12303}
12304
12305/**
12306 * @note Locks mParent + this object for writing.
12307 */
12308STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12309{
12310 LogFlowThisFunc(("\n"));
12311
12312 AutoCaller autoCaller(this);
12313 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12314
12315 /* endSavingState() need mParent lock */
12316 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12317
12318 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12319 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12320 && mConsoleTaskData.mLastState != MachineState_Null
12321 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12322 E_FAIL);
12323
12324 /*
12325 * On failure, set the state to the state we had when BeginSavingState()
12326 * was called (this is expected by Console::SaveState() and the associated
12327 * task). On success the VM process already changed the state to
12328 * MachineState_Saved, so no need to do anything.
12329 */
12330 if (FAILED(iResult))
12331 setMachineState(mConsoleTaskData.mLastState);
12332
12333 return endSavingState(iResult, aErrMsg);
12334}
12335
12336/**
12337 * @note Locks this object for writing.
12338 */
12339STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12340{
12341 LogFlowThisFunc(("\n"));
12342
12343 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12344
12345 AutoCaller autoCaller(this);
12346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12347
12348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12349
12350 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12351 || mData->mMachineState == MachineState_Teleported
12352 || mData->mMachineState == MachineState_Aborted
12353 , E_FAIL); /** @todo setError. */
12354
12355 Utf8Str stateFilePathFull = aSavedStateFile;
12356 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12357 if (RT_FAILURE(vrc))
12358 return setError(VBOX_E_FILE_ERROR,
12359 tr("Invalid saved state file path '%ls' (%Rrc)"),
12360 aSavedStateFile,
12361 vrc);
12362
12363 mSSData->strStateFilePath = stateFilePathFull;
12364
12365 /* The below setMachineState() will detect the state transition and will
12366 * update the settings file */
12367
12368 return setMachineState(MachineState_Saved);
12369}
12370
12371STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12372 ComSafeArrayOut(BSTR, aValues),
12373 ComSafeArrayOut(LONG64, aTimestamps),
12374 ComSafeArrayOut(BSTR, aFlags))
12375{
12376 LogFlowThisFunc(("\n"));
12377
12378#ifdef VBOX_WITH_GUEST_PROPS
12379 using namespace guestProp;
12380
12381 AutoCaller autoCaller(this);
12382 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12383
12384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12385
12386 CheckComArgOutSafeArrayPointerValid(aNames);
12387 CheckComArgOutSafeArrayPointerValid(aValues);
12388 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12389 CheckComArgOutSafeArrayPointerValid(aFlags);
12390
12391 size_t cEntries = mHWData->mGuestProperties.size();
12392 com::SafeArray<BSTR> names(cEntries);
12393 com::SafeArray<BSTR> values(cEntries);
12394 com::SafeArray<LONG64> timestamps(cEntries);
12395 com::SafeArray<BSTR> flags(cEntries);
12396 unsigned i = 0;
12397 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12398 it != mHWData->mGuestProperties.end();
12399 ++it)
12400 {
12401 char szFlags[MAX_FLAGS_LEN + 1];
12402 it->strName.cloneTo(&names[i]);
12403 it->strValue.cloneTo(&values[i]);
12404 timestamps[i] = it->mTimestamp;
12405 /* If it is NULL, keep it NULL. */
12406 if (it->mFlags)
12407 {
12408 writeFlags(it->mFlags, szFlags);
12409 Bstr(szFlags).cloneTo(&flags[i]);
12410 }
12411 else
12412 flags[i] = NULL;
12413 ++i;
12414 }
12415 names.detachTo(ComSafeArrayOutArg(aNames));
12416 values.detachTo(ComSafeArrayOutArg(aValues));
12417 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12418 flags.detachTo(ComSafeArrayOutArg(aFlags));
12419 return S_OK;
12420#else
12421 ReturnComNotImplemented();
12422#endif
12423}
12424
12425STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12426 IN_BSTR aValue,
12427 LONG64 aTimestamp,
12428 IN_BSTR aFlags)
12429{
12430 LogFlowThisFunc(("\n"));
12431
12432#ifdef VBOX_WITH_GUEST_PROPS
12433 using namespace guestProp;
12434
12435 CheckComArgStrNotEmptyOrNull(aName);
12436 CheckComArgNotNull(aValue);
12437 CheckComArgNotNull(aFlags);
12438
12439 try
12440 {
12441 /*
12442 * Convert input up front.
12443 */
12444 Utf8Str utf8Name(aName);
12445 uint32_t fFlags = NILFLAG;
12446 if (aFlags)
12447 {
12448 Utf8Str utf8Flags(aFlags);
12449 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12450 AssertRCReturn(vrc, E_INVALIDARG);
12451 }
12452
12453 /*
12454 * Now grab the object lock, validate the state and do the update.
12455 */
12456 AutoCaller autoCaller(this);
12457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12458
12459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12460
12461 switch (mData->mMachineState)
12462 {
12463 case MachineState_Paused:
12464 case MachineState_Running:
12465 case MachineState_Teleporting:
12466 case MachineState_TeleportingPausedVM:
12467 case MachineState_LiveSnapshotting:
12468 case MachineState_DeletingSnapshotOnline:
12469 case MachineState_DeletingSnapshotPaused:
12470 case MachineState_Saving:
12471 break;
12472
12473 default:
12474#ifndef DEBUG_sunlover
12475 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12476 VBOX_E_INVALID_VM_STATE);
12477#else
12478 return VBOX_E_INVALID_VM_STATE;
12479#endif
12480 }
12481
12482 setModified(IsModified_MachineData);
12483 mHWData.backup();
12484
12485 /** @todo r=bird: The careful memory handling doesn't work out here because
12486 * the catch block won't undo any damage we've done. So, if push_back throws
12487 * bad_alloc then you've lost the value.
12488 *
12489 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12490 * since values that changes actually bubbles to the end of the list. Using
12491 * something that has an efficient lookup and can tolerate a bit of updates
12492 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12493 * combination of RTStrCache (for sharing names and getting uniqueness into
12494 * the bargain) and hash/tree is another. */
12495 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12496 iter != mHWData->mGuestProperties.end();
12497 ++iter)
12498 if (utf8Name == iter->strName)
12499 {
12500 mHWData->mGuestProperties.erase(iter);
12501 mData->mGuestPropertiesModified = TRUE;
12502 break;
12503 }
12504 if (aValue != NULL)
12505 {
12506 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12507 mHWData->mGuestProperties.push_back(property);
12508 mData->mGuestPropertiesModified = TRUE;
12509 }
12510
12511 /*
12512 * Send a callback notification if appropriate
12513 */
12514 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12515 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12516 RTSTR_MAX,
12517 utf8Name.c_str(),
12518 RTSTR_MAX, NULL)
12519 )
12520 {
12521 alock.release();
12522
12523 mParent->onGuestPropertyChange(mData->mUuid,
12524 aName,
12525 aValue,
12526 aFlags);
12527 }
12528 }
12529 catch (...)
12530 {
12531 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12532 }
12533 return S_OK;
12534#else
12535 ReturnComNotImplemented();
12536#endif
12537}
12538
12539STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12540 IMediumAttachment **aNewAttachment)
12541{
12542 CheckComArgNotNull(aAttachment);
12543 CheckComArgOutPointerValid(aNewAttachment);
12544
12545 AutoCaller autoCaller(this);
12546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12547
12548 // request the host lock first, since might be calling Host methods for getting host drives;
12549 // next, protect the media tree all the while we're in here, as well as our member variables
12550 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12551 this->lockHandle(),
12552 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12553
12554 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12555
12556 Bstr ctrlName;
12557 LONG lPort;
12558 LONG lDevice;
12559 bool fTempEject;
12560 {
12561 AutoCaller autoAttachCaller(this);
12562 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12563
12564 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12565
12566 /* Need to query the details first, as the IMediumAttachment reference
12567 * might be to the original settings, which we are going to change. */
12568 ctrlName = pAttach->getControllerName();
12569 lPort = pAttach->getPort();
12570 lDevice = pAttach->getDevice();
12571 fTempEject = pAttach->getTempEject();
12572 }
12573
12574 if (!fTempEject)
12575 {
12576 /* Remember previously mounted medium. The medium before taking the
12577 * backup is not necessarily the same thing. */
12578 ComObjPtr<Medium> oldmedium;
12579 oldmedium = pAttach->getMedium();
12580
12581 setModified(IsModified_Storage);
12582 mMediaData.backup();
12583
12584 // The backup operation makes the pAttach reference point to the
12585 // old settings. Re-get the correct reference.
12586 pAttach = findAttachment(mMediaData->mAttachments,
12587 ctrlName.raw(),
12588 lPort,
12589 lDevice);
12590
12591 {
12592 AutoCaller autoAttachCaller(this);
12593 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12594
12595 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12596 if (!oldmedium.isNull())
12597 oldmedium->removeBackReference(mData->mUuid);
12598
12599 pAttach->updateMedium(NULL);
12600 pAttach->updateEjected();
12601 }
12602
12603 setModified(IsModified_Storage);
12604 }
12605 else
12606 {
12607 {
12608 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12609 pAttach->updateEjected();
12610 }
12611 }
12612
12613 pAttach.queryInterfaceTo(aNewAttachment);
12614
12615 return S_OK;
12616}
12617
12618// public methods only for internal purposes
12619/////////////////////////////////////////////////////////////////////////////
12620
12621/**
12622 * Called from the client watcher thread to check for expected or unexpected
12623 * death of the client process that has a direct session to this machine.
12624 *
12625 * On Win32 and on OS/2, this method is called only when we've got the
12626 * mutex (i.e. the client has either died or terminated normally) so it always
12627 * returns @c true (the client is terminated, the session machine is
12628 * uninitialized).
12629 *
12630 * On other platforms, the method returns @c true if the client process has
12631 * terminated normally or abnormally and the session machine was uninitialized,
12632 * and @c false if the client process is still alive.
12633 *
12634 * @note Locks this object for writing.
12635 */
12636bool SessionMachine::checkForDeath()
12637{
12638 Uninit::Reason reason;
12639 bool terminated = false;
12640
12641 /* Enclose autoCaller with a block because calling uninit() from under it
12642 * will deadlock. */
12643 {
12644 AutoCaller autoCaller(this);
12645 if (!autoCaller.isOk())
12646 {
12647 /* return true if not ready, to cause the client watcher to exclude
12648 * the corresponding session from watching */
12649 LogFlowThisFunc(("Already uninitialized!\n"));
12650 return true;
12651 }
12652
12653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12654
12655 /* Determine the reason of death: if the session state is Closing here,
12656 * everything is fine. Otherwise it means that the client did not call
12657 * OnSessionEnd() before it released the IPC semaphore. This may happen
12658 * either because the client process has abnormally terminated, or
12659 * because it simply forgot to call ISession::Close() before exiting. We
12660 * threat the latter also as an abnormal termination (see
12661 * Session::uninit() for details). */
12662 reason = mData->mSession.mState == SessionState_Unlocking ?
12663 Uninit::Normal :
12664 Uninit::Abnormal;
12665
12666#if defined(RT_OS_WINDOWS)
12667
12668 AssertMsg(mIPCSem, ("semaphore must be created"));
12669
12670 /* release the IPC mutex */
12671 ::ReleaseMutex(mIPCSem);
12672
12673 terminated = true;
12674
12675#elif defined(RT_OS_OS2)
12676
12677 AssertMsg(mIPCSem, ("semaphore must be created"));
12678
12679 /* release the IPC mutex */
12680 ::DosReleaseMutexSem(mIPCSem);
12681
12682 terminated = true;
12683
12684#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12685
12686 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12687
12688 int val = ::semctl(mIPCSem, 0, GETVAL);
12689 if (val > 0)
12690 {
12691 /* the semaphore is signaled, meaning the session is terminated */
12692 terminated = true;
12693 }
12694
12695#else
12696# error "Port me!"
12697#endif
12698
12699 } /* AutoCaller block */
12700
12701 if (terminated)
12702 uninit(reason);
12703
12704 return terminated;
12705}
12706
12707/**
12708 * @note Locks this object for reading.
12709 */
12710HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12711{
12712 LogFlowThisFunc(("\n"));
12713
12714 AutoCaller autoCaller(this);
12715 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12716
12717 ComPtr<IInternalSessionControl> directControl;
12718 {
12719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12720 directControl = mData->mSession.mDirectControl;
12721 }
12722
12723 /* ignore notifications sent after #OnSessionEnd() is called */
12724 if (!directControl)
12725 return S_OK;
12726
12727 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12728}
12729
12730/**
12731 * @note Locks this object for reading.
12732 */
12733HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12734 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12735{
12736 LogFlowThisFunc(("\n"));
12737
12738 AutoCaller autoCaller(this);
12739 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12740
12741 ComPtr<IInternalSessionControl> directControl;
12742 {
12743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12744 directControl = mData->mSession.mDirectControl;
12745 }
12746
12747 /* ignore notifications sent after #OnSessionEnd() is called */
12748 if (!directControl)
12749 return S_OK;
12750 /*
12751 * instead acting like callback we ask IVirtualBox deliver corresponding event
12752 */
12753
12754 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12755 return S_OK;
12756}
12757
12758/**
12759 * @note Locks this object for reading.
12760 */
12761HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12762{
12763 LogFlowThisFunc(("\n"));
12764
12765 AutoCaller autoCaller(this);
12766 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12767
12768 ComPtr<IInternalSessionControl> directControl;
12769 {
12770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12771 directControl = mData->mSession.mDirectControl;
12772 }
12773
12774 /* ignore notifications sent after #OnSessionEnd() is called */
12775 if (!directControl)
12776 return S_OK;
12777
12778 return directControl->OnSerialPortChange(serialPort);
12779}
12780
12781/**
12782 * @note Locks this object for reading.
12783 */
12784HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12785{
12786 LogFlowThisFunc(("\n"));
12787
12788 AutoCaller autoCaller(this);
12789 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12790
12791 ComPtr<IInternalSessionControl> directControl;
12792 {
12793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12794 directControl = mData->mSession.mDirectControl;
12795 }
12796
12797 /* ignore notifications sent after #OnSessionEnd() is called */
12798 if (!directControl)
12799 return S_OK;
12800
12801 return directControl->OnParallelPortChange(parallelPort);
12802}
12803
12804/**
12805 * @note Locks this object for reading.
12806 */
12807HRESULT SessionMachine::onStorageControllerChange()
12808{
12809 LogFlowThisFunc(("\n"));
12810
12811 AutoCaller autoCaller(this);
12812 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12813
12814 ComPtr<IInternalSessionControl> directControl;
12815 {
12816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12817 directControl = mData->mSession.mDirectControl;
12818 }
12819
12820 /* ignore notifications sent after #OnSessionEnd() is called */
12821 if (!directControl)
12822 return S_OK;
12823
12824 return directControl->OnStorageControllerChange();
12825}
12826
12827/**
12828 * @note Locks this object for reading.
12829 */
12830HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12831{
12832 LogFlowThisFunc(("\n"));
12833
12834 AutoCaller autoCaller(this);
12835 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12836
12837 ComPtr<IInternalSessionControl> directControl;
12838 {
12839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12840 directControl = mData->mSession.mDirectControl;
12841 }
12842
12843 /* ignore notifications sent after #OnSessionEnd() is called */
12844 if (!directControl)
12845 return S_OK;
12846
12847 return directControl->OnMediumChange(aAttachment, aForce);
12848}
12849
12850/**
12851 * @note Locks this object for reading.
12852 */
12853HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12854{
12855 LogFlowThisFunc(("\n"));
12856
12857 AutoCaller autoCaller(this);
12858 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12859
12860 ComPtr<IInternalSessionControl> directControl;
12861 {
12862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12863 directControl = mData->mSession.mDirectControl;
12864 }
12865
12866 /* ignore notifications sent after #OnSessionEnd() is called */
12867 if (!directControl)
12868 return S_OK;
12869
12870 return directControl->OnCPUChange(aCPU, aRemove);
12871}
12872
12873HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12874{
12875 LogFlowThisFunc(("\n"));
12876
12877 AutoCaller autoCaller(this);
12878 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12879
12880 ComPtr<IInternalSessionControl> directControl;
12881 {
12882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12883 directControl = mData->mSession.mDirectControl;
12884 }
12885
12886 /* ignore notifications sent after #OnSessionEnd() is called */
12887 if (!directControl)
12888 return S_OK;
12889
12890 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12891}
12892
12893/**
12894 * @note Locks this object for reading.
12895 */
12896HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12897{
12898 LogFlowThisFunc(("\n"));
12899
12900 AutoCaller autoCaller(this);
12901 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12902
12903 ComPtr<IInternalSessionControl> directControl;
12904 {
12905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12906 directControl = mData->mSession.mDirectControl;
12907 }
12908
12909 /* ignore notifications sent after #OnSessionEnd() is called */
12910 if (!directControl)
12911 return S_OK;
12912
12913 return directControl->OnVRDEServerChange(aRestart);
12914}
12915
12916/**
12917 * @note Locks this object for reading.
12918 */
12919HRESULT SessionMachine::onUSBControllerChange()
12920{
12921 LogFlowThisFunc(("\n"));
12922
12923 AutoCaller autoCaller(this);
12924 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12925
12926 ComPtr<IInternalSessionControl> directControl;
12927 {
12928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12929 directControl = mData->mSession.mDirectControl;
12930 }
12931
12932 /* ignore notifications sent after #OnSessionEnd() is called */
12933 if (!directControl)
12934 return S_OK;
12935
12936 return directControl->OnUSBControllerChange();
12937}
12938
12939/**
12940 * @note Locks this object for reading.
12941 */
12942HRESULT SessionMachine::onSharedFolderChange()
12943{
12944 LogFlowThisFunc(("\n"));
12945
12946 AutoCaller autoCaller(this);
12947 AssertComRCReturnRC(autoCaller.rc());
12948
12949 ComPtr<IInternalSessionControl> directControl;
12950 {
12951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12952 directControl = mData->mSession.mDirectControl;
12953 }
12954
12955 /* ignore notifications sent after #OnSessionEnd() is called */
12956 if (!directControl)
12957 return S_OK;
12958
12959 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12960}
12961
12962/**
12963 * @note Locks this object for reading.
12964 */
12965HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12966{
12967 LogFlowThisFunc(("\n"));
12968
12969 AutoCaller autoCaller(this);
12970 AssertComRCReturnRC(autoCaller.rc());
12971
12972 ComPtr<IInternalSessionControl> directControl;
12973 {
12974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12975 directControl = mData->mSession.mDirectControl;
12976 }
12977
12978 /* ignore notifications sent after #OnSessionEnd() is called */
12979 if (!directControl)
12980 return S_OK;
12981
12982 return directControl->OnClipboardModeChange(aClipboardMode);
12983}
12984
12985/**
12986 * @note Locks this object for reading.
12987 */
12988HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
12989{
12990 LogFlowThisFunc(("\n"));
12991
12992 AutoCaller autoCaller(this);
12993 AssertComRCReturnRC(autoCaller.rc());
12994
12995 ComPtr<IInternalSessionControl> directControl;
12996 {
12997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12998 directControl = mData->mSession.mDirectControl;
12999 }
13000
13001 /* ignore notifications sent after #OnSessionEnd() is called */
13002 if (!directControl)
13003 return S_OK;
13004
13005 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13006}
13007
13008/**
13009 * @note Locks this object for reading.
13010 */
13011HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13012{
13013 LogFlowThisFunc(("\n"));
13014
13015 AutoCaller autoCaller(this);
13016 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13017
13018 ComPtr<IInternalSessionControl> directControl;
13019 {
13020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13021 directControl = mData->mSession.mDirectControl;
13022 }
13023
13024 /* ignore notifications sent after #OnSessionEnd() is called */
13025 if (!directControl)
13026 return S_OK;
13027
13028 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13029}
13030
13031/**
13032 * @note Locks this object for reading.
13033 */
13034HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13035{
13036 LogFlowThisFunc(("\n"));
13037
13038 AutoCaller autoCaller(this);
13039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13040
13041 ComPtr<IInternalSessionControl> directControl;
13042 {
13043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13044 directControl = mData->mSession.mDirectControl;
13045 }
13046
13047 /* ignore notifications sent after #OnSessionEnd() is called */
13048 if (!directControl)
13049 return S_OK;
13050
13051 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13052}
13053
13054/**
13055 * Returns @c true if this machine's USB controller reports it has a matching
13056 * filter for the given USB device and @c false otherwise.
13057 *
13058 * @note locks this object for reading.
13059 */
13060bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13061{
13062 AutoCaller autoCaller(this);
13063 /* silently return if not ready -- this method may be called after the
13064 * direct machine session has been called */
13065 if (!autoCaller.isOk())
13066 return false;
13067
13068#ifdef VBOX_WITH_USB
13069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13070
13071 switch (mData->mMachineState)
13072 {
13073 case MachineState_Starting:
13074 case MachineState_Restoring:
13075 case MachineState_TeleportingIn:
13076 case MachineState_Paused:
13077 case MachineState_Running:
13078 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13079 * elsewhere... */
13080 alock.release();
13081 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13082 default: break;
13083 }
13084#else
13085 NOREF(aDevice);
13086 NOREF(aMaskedIfs);
13087#endif
13088 return false;
13089}
13090
13091/**
13092 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13093 */
13094HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13095 IVirtualBoxErrorInfo *aError,
13096 ULONG aMaskedIfs)
13097{
13098 LogFlowThisFunc(("\n"));
13099
13100 AutoCaller autoCaller(this);
13101
13102 /* This notification may happen after the machine object has been
13103 * uninitialized (the session was closed), so don't assert. */
13104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13105
13106 ComPtr<IInternalSessionControl> directControl;
13107 {
13108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13109 directControl = mData->mSession.mDirectControl;
13110 }
13111
13112 /* fail on notifications sent after #OnSessionEnd() is called, it is
13113 * expected by the caller */
13114 if (!directControl)
13115 return E_FAIL;
13116
13117 /* No locks should be held at this point. */
13118 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13119 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13120
13121 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13122}
13123
13124/**
13125 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13126 */
13127HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13128 IVirtualBoxErrorInfo *aError)
13129{
13130 LogFlowThisFunc(("\n"));
13131
13132 AutoCaller autoCaller(this);
13133
13134 /* This notification may happen after the machine object has been
13135 * uninitialized (the session was closed), so don't assert. */
13136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13137
13138 ComPtr<IInternalSessionControl> directControl;
13139 {
13140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13141 directControl = mData->mSession.mDirectControl;
13142 }
13143
13144 /* fail on notifications sent after #OnSessionEnd() is called, it is
13145 * expected by the caller */
13146 if (!directControl)
13147 return E_FAIL;
13148
13149 /* No locks should be held at this point. */
13150 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13151 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13152
13153 return directControl->OnUSBDeviceDetach(aId, aError);
13154}
13155
13156// protected methods
13157/////////////////////////////////////////////////////////////////////////////
13158
13159/**
13160 * Helper method to finalize saving the state.
13161 *
13162 * @note Must be called from under this object's lock.
13163 *
13164 * @param aRc S_OK if the snapshot has been taken successfully
13165 * @param aErrMsg human readable error message for failure
13166 *
13167 * @note Locks mParent + this objects for writing.
13168 */
13169HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13170{
13171 LogFlowThisFuncEnter();
13172
13173 AutoCaller autoCaller(this);
13174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13175
13176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13177
13178 HRESULT rc = S_OK;
13179
13180 if (SUCCEEDED(aRc))
13181 {
13182 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13183
13184 /* save all VM settings */
13185 rc = saveSettings(NULL);
13186 // no need to check whether VirtualBox.xml needs saving also since
13187 // we can't have a name change pending at this point
13188 }
13189 else
13190 {
13191 // delete the saved state file (it might have been already created);
13192 // we need not check whether this is shared with a snapshot here because
13193 // we certainly created this saved state file here anew
13194 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13195 }
13196
13197 /* notify the progress object about operation completion */
13198 Assert(mConsoleTaskData.mProgress);
13199 if (SUCCEEDED(aRc))
13200 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13201 else
13202 {
13203 if (aErrMsg.length())
13204 mConsoleTaskData.mProgress->notifyComplete(aRc,
13205 COM_IIDOF(ISession),
13206 getComponentName(),
13207 aErrMsg.c_str());
13208 else
13209 mConsoleTaskData.mProgress->notifyComplete(aRc);
13210 }
13211
13212 /* clear out the temporary saved state data */
13213 mConsoleTaskData.mLastState = MachineState_Null;
13214 mConsoleTaskData.strStateFilePath.setNull();
13215 mConsoleTaskData.mProgress.setNull();
13216
13217 LogFlowThisFuncLeave();
13218 return rc;
13219}
13220
13221/**
13222 * Deletes the given file if it is no longer in use by either the current machine state
13223 * (if the machine is "saved") or any of the machine's snapshots.
13224 *
13225 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13226 * but is different for each SnapshotMachine. When calling this, the order of calling this
13227 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13228 * is therefore critical. I know, it's all rather messy.
13229 *
13230 * @param strStateFile
13231 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13232 */
13233void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13234 Snapshot *pSnapshotToIgnore)
13235{
13236 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13237 if ( (strStateFile.isNotEmpty())
13238 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13239 )
13240 // ... and it must also not be shared with other snapshots
13241 if ( !mData->mFirstSnapshot
13242 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13243 // this checks the SnapshotMachine's state file paths
13244 )
13245 RTFileDelete(strStateFile.c_str());
13246}
13247
13248/**
13249 * Locks the attached media.
13250 *
13251 * All attached hard disks are locked for writing and DVD/floppy are locked for
13252 * reading. Parents of attached hard disks (if any) are locked for reading.
13253 *
13254 * This method also performs accessibility check of all media it locks: if some
13255 * media is inaccessible, the method will return a failure and a bunch of
13256 * extended error info objects per each inaccessible medium.
13257 *
13258 * Note that this method is atomic: if it returns a success, all media are
13259 * locked as described above; on failure no media is locked at all (all
13260 * succeeded individual locks will be undone).
13261 *
13262 * This method is intended to be called when the machine is in Starting or
13263 * Restoring state and asserts otherwise.
13264 *
13265 * The locks made by this method must be undone by calling #unlockMedia() when
13266 * no more needed.
13267 */
13268HRESULT SessionMachine::lockMedia()
13269{
13270 AutoCaller autoCaller(this);
13271 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13272
13273 AutoMultiWriteLock2 alock(this->lockHandle(),
13274 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13275
13276 AssertReturn( mData->mMachineState == MachineState_Starting
13277 || mData->mMachineState == MachineState_Restoring
13278 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13279 /* bail out if trying to lock things with already set up locking */
13280 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13281
13282 clearError();
13283 MultiResult mrc(S_OK);
13284
13285 /* Collect locking information for all medium objects attached to the VM. */
13286 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13287 it != mMediaData->mAttachments.end();
13288 ++it)
13289 {
13290 MediumAttachment* pAtt = *it;
13291 DeviceType_T devType = pAtt->getType();
13292 Medium *pMedium = pAtt->getMedium();
13293
13294 MediumLockList *pMediumLockList(new MediumLockList());
13295 // There can be attachments without a medium (floppy/dvd), and thus
13296 // it's impossible to create a medium lock list. It still makes sense
13297 // to have the empty medium lock list in the map in case a medium is
13298 // attached later.
13299 if (pMedium != NULL)
13300 {
13301 MediumType_T mediumType = pMedium->getType();
13302 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13303 || mediumType == MediumType_Shareable;
13304 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13305
13306 alock.release();
13307 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13308 !fIsReadOnlyLock /* fMediumLockWrite */,
13309 NULL,
13310 *pMediumLockList);
13311 alock.acquire();
13312 if (FAILED(mrc))
13313 {
13314 delete pMediumLockList;
13315 mData->mSession.mLockedMedia.Clear();
13316 break;
13317 }
13318 }
13319
13320 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13321 if (FAILED(rc))
13322 {
13323 mData->mSession.mLockedMedia.Clear();
13324 mrc = setError(rc,
13325 tr("Collecting locking information for all attached media failed"));
13326 break;
13327 }
13328 }
13329
13330 if (SUCCEEDED(mrc))
13331 {
13332 /* Now lock all media. If this fails, nothing is locked. */
13333 alock.release();
13334 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13335 alock.acquire();
13336 if (FAILED(rc))
13337 {
13338 mrc = setError(rc,
13339 tr("Locking of attached media failed"));
13340 }
13341 }
13342
13343 return mrc;
13344}
13345
13346/**
13347 * Undoes the locks made by by #lockMedia().
13348 */
13349void SessionMachine::unlockMedia()
13350{
13351 AutoCaller autoCaller(this);
13352 AssertComRCReturnVoid(autoCaller.rc());
13353
13354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13355
13356 /* we may be holding important error info on the current thread;
13357 * preserve it */
13358 ErrorInfoKeeper eik;
13359
13360 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13361 AssertComRC(rc);
13362}
13363
13364/**
13365 * Helper to change the machine state (reimplementation).
13366 *
13367 * @note Locks this object for writing.
13368 * @note This method must not call saveSettings or SaveSettings, otherwise
13369 * it can cause crashes in random places due to unexpectedly committing
13370 * the current settings. The caller is responsible for that. The call
13371 * to saveStateSettings is fine, because this method does not commit.
13372 */
13373HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13374{
13375 LogFlowThisFuncEnter();
13376 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13377
13378 AutoCaller autoCaller(this);
13379 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13380
13381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13382
13383 MachineState_T oldMachineState = mData->mMachineState;
13384
13385 AssertMsgReturn(oldMachineState != aMachineState,
13386 ("oldMachineState=%s, aMachineState=%s\n",
13387 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13388 E_FAIL);
13389
13390 HRESULT rc = S_OK;
13391
13392 int stsFlags = 0;
13393 bool deleteSavedState = false;
13394
13395 /* detect some state transitions */
13396
13397 if ( ( oldMachineState == MachineState_Saved
13398 && aMachineState == MachineState_Restoring)
13399 || ( ( oldMachineState == MachineState_PoweredOff
13400 || oldMachineState == MachineState_Teleported
13401 || oldMachineState == MachineState_Aborted
13402 )
13403 && ( aMachineState == MachineState_TeleportingIn
13404 || aMachineState == MachineState_Starting
13405 )
13406 )
13407 )
13408 {
13409 /* The EMT thread is about to start */
13410
13411 /* Nothing to do here for now... */
13412
13413 /// @todo NEWMEDIA don't let mDVDDrive and other children
13414 /// change anything when in the Starting/Restoring state
13415 }
13416 else if ( ( oldMachineState == MachineState_Running
13417 || oldMachineState == MachineState_Paused
13418 || oldMachineState == MachineState_Teleporting
13419 || oldMachineState == MachineState_LiveSnapshotting
13420 || oldMachineState == MachineState_Stuck
13421 || oldMachineState == MachineState_Starting
13422 || oldMachineState == MachineState_Stopping
13423 || oldMachineState == MachineState_Saving
13424 || oldMachineState == MachineState_Restoring
13425 || oldMachineState == MachineState_TeleportingPausedVM
13426 || oldMachineState == MachineState_TeleportingIn
13427 )
13428 && ( aMachineState == MachineState_PoweredOff
13429 || aMachineState == MachineState_Saved
13430 || aMachineState == MachineState_Teleported
13431 || aMachineState == MachineState_Aborted
13432 )
13433 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13434 * snapshot */
13435 && ( mConsoleTaskData.mSnapshot.isNull()
13436 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13437 )
13438 )
13439 {
13440 /* The EMT thread has just stopped, unlock attached media. Note that as
13441 * opposed to locking that is done from Console, we do unlocking here
13442 * because the VM process may have aborted before having a chance to
13443 * properly unlock all media it locked. */
13444
13445 unlockMedia();
13446 }
13447
13448 if (oldMachineState == MachineState_Restoring)
13449 {
13450 if (aMachineState != MachineState_Saved)
13451 {
13452 /*
13453 * delete the saved state file once the machine has finished
13454 * restoring from it (note that Console sets the state from
13455 * Restoring to Saved if the VM couldn't restore successfully,
13456 * to give the user an ability to fix an error and retry --
13457 * we keep the saved state file in this case)
13458 */
13459 deleteSavedState = true;
13460 }
13461 }
13462 else if ( oldMachineState == MachineState_Saved
13463 && ( aMachineState == MachineState_PoweredOff
13464 || aMachineState == MachineState_Aborted
13465 || aMachineState == MachineState_Teleported
13466 )
13467 )
13468 {
13469 /*
13470 * delete the saved state after Console::ForgetSavedState() is called
13471 * or if the VM process (owning a direct VM session) crashed while the
13472 * VM was Saved
13473 */
13474
13475 /// @todo (dmik)
13476 // Not sure that deleting the saved state file just because of the
13477 // client death before it attempted to restore the VM is a good
13478 // thing. But when it crashes we need to go to the Aborted state
13479 // which cannot have the saved state file associated... The only
13480 // way to fix this is to make the Aborted condition not a VM state
13481 // but a bool flag: i.e., when a crash occurs, set it to true and
13482 // change the state to PoweredOff or Saved depending on the
13483 // saved state presence.
13484
13485 deleteSavedState = true;
13486 mData->mCurrentStateModified = TRUE;
13487 stsFlags |= SaveSTS_CurStateModified;
13488 }
13489
13490 if ( aMachineState == MachineState_Starting
13491 || aMachineState == MachineState_Restoring
13492 || aMachineState == MachineState_TeleportingIn
13493 )
13494 {
13495 /* set the current state modified flag to indicate that the current
13496 * state is no more identical to the state in the
13497 * current snapshot */
13498 if (!mData->mCurrentSnapshot.isNull())
13499 {
13500 mData->mCurrentStateModified = TRUE;
13501 stsFlags |= SaveSTS_CurStateModified;
13502 }
13503 }
13504
13505 if (deleteSavedState)
13506 {
13507 if (mRemoveSavedState)
13508 {
13509 Assert(!mSSData->strStateFilePath.isEmpty());
13510
13511 // it is safe to delete the saved state file if ...
13512 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13513 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13514 // ... none of the snapshots share the saved state file
13515 )
13516 RTFileDelete(mSSData->strStateFilePath.c_str());
13517 }
13518
13519 mSSData->strStateFilePath.setNull();
13520 stsFlags |= SaveSTS_StateFilePath;
13521 }
13522
13523 /* redirect to the underlying peer machine */
13524 mPeer->setMachineState(aMachineState);
13525
13526 if ( aMachineState == MachineState_PoweredOff
13527 || aMachineState == MachineState_Teleported
13528 || aMachineState == MachineState_Aborted
13529 || aMachineState == MachineState_Saved)
13530 {
13531 /* the machine has stopped execution
13532 * (or the saved state file was adopted) */
13533 stsFlags |= SaveSTS_StateTimeStamp;
13534 }
13535
13536 if ( ( oldMachineState == MachineState_PoweredOff
13537 || oldMachineState == MachineState_Aborted
13538 || oldMachineState == MachineState_Teleported
13539 )
13540 && aMachineState == MachineState_Saved)
13541 {
13542 /* the saved state file was adopted */
13543 Assert(!mSSData->strStateFilePath.isEmpty());
13544 stsFlags |= SaveSTS_StateFilePath;
13545 }
13546
13547#ifdef VBOX_WITH_GUEST_PROPS
13548 if ( aMachineState == MachineState_PoweredOff
13549 || aMachineState == MachineState_Aborted
13550 || aMachineState == MachineState_Teleported)
13551 {
13552 /* Make sure any transient guest properties get removed from the
13553 * property store on shutdown. */
13554
13555 HWData::GuestPropertyList::iterator it;
13556 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13557 if (!fNeedsSaving)
13558 for (it = mHWData->mGuestProperties.begin();
13559 it != mHWData->mGuestProperties.end(); ++it)
13560 if ( (it->mFlags & guestProp::TRANSIENT)
13561 || (it->mFlags & guestProp::TRANSRESET))
13562 {
13563 fNeedsSaving = true;
13564 break;
13565 }
13566 if (fNeedsSaving)
13567 {
13568 mData->mCurrentStateModified = TRUE;
13569 stsFlags |= SaveSTS_CurStateModified;
13570 }
13571 }
13572#endif
13573
13574 rc = saveStateSettings(stsFlags);
13575
13576 if ( ( oldMachineState != MachineState_PoweredOff
13577 && oldMachineState != MachineState_Aborted
13578 && oldMachineState != MachineState_Teleported
13579 )
13580 && ( aMachineState == MachineState_PoweredOff
13581 || aMachineState == MachineState_Aborted
13582 || aMachineState == MachineState_Teleported
13583 )
13584 )
13585 {
13586 /* we've been shut down for any reason */
13587 /* no special action so far */
13588 }
13589
13590 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13591 LogFlowThisFuncLeave();
13592 return rc;
13593}
13594
13595/**
13596 * Sends the current machine state value to the VM process.
13597 *
13598 * @note Locks this object for reading, then calls a client process.
13599 */
13600HRESULT SessionMachine::updateMachineStateOnClient()
13601{
13602 AutoCaller autoCaller(this);
13603 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13604
13605 ComPtr<IInternalSessionControl> directControl;
13606 {
13607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13608 AssertReturn(!!mData, E_FAIL);
13609 directControl = mData->mSession.mDirectControl;
13610
13611 /* directControl may be already set to NULL here in #OnSessionEnd()
13612 * called too early by the direct session process while there is still
13613 * some operation (like deleting the snapshot) in progress. The client
13614 * process in this case is waiting inside Session::close() for the
13615 * "end session" process object to complete, while #uninit() called by
13616 * #checkForDeath() on the Watcher thread is waiting for the pending
13617 * operation to complete. For now, we accept this inconsistent behavior
13618 * and simply do nothing here. */
13619
13620 if (mData->mSession.mState == SessionState_Unlocking)
13621 return S_OK;
13622
13623 AssertReturn(!directControl.isNull(), E_FAIL);
13624 }
13625
13626 return directControl->UpdateMachineState(mData->mMachineState);
13627}
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