VirtualBox

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

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

Elinate use of NULL object references in the API - 6124

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