VirtualBox

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

Last change on this file since 45563 was 45563, checked in by vboxsync, 12 years ago

Main: Make SilentReconfigureWhilePaused a per VM flag

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 473.8 KB
Line 
1/* $Id: MachineImpl.cpp 45563 2013-04-16 11:13:16Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#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 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = false;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mSyntheticCpu = false;
194 mHPETEnabled = false;
195
196 /* default boot order: floppy - DVD - HDD */
197 mBootOrder[0] = DeviceType_Floppy;
198 mBootOrder[1] = DeviceType_DVD;
199 mBootOrder[2] = DeviceType_HardDisk;
200 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
201 mBootOrder[i] = DeviceType_Null;
202
203 mClipboardMode = ClipboardMode_Disabled;
204 mDragAndDropMode = DragAndDropMode_Disabled;
205 mGuestPropertyNotificationPatterns = "";
206
207 mFirmwareType = FirmwareType_BIOS;
208 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
209 mPointingHIDType = PointingHIDType_PS2Mouse;
210 mChipsetType = ChipsetType_PIIX3;
211 mEmulatedUSBWebcamEnabled = FALSE;
212 mEmulatedUSBCardReaderEnabled = FALSE;
213
214 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
215 mCPUAttached[i] = false;
216
217 mIOCacheEnabled = true;
218 mIOCacheSize = 5; /* 5MB */
219
220 /* Maximum CPU execution cap by default. */
221 mCpuExecutionCap = 100;
222}
223
224Machine::HWData::~HWData()
225{
226}
227
228/////////////////////////////////////////////////////////////////////////////
229// Machine::HDData structure
230/////////////////////////////////////////////////////////////////////////////
231
232Machine::MediaData::MediaData()
233{
234}
235
236Machine::MediaData::~MediaData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine()
248 : mCollectorGuest(NULL),
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 *
285 * @return Success indicator. if not S_OK, the machine object is invalid
286 */
287HRESULT Machine::init(VirtualBox *aParent,
288 const Utf8Str &strConfigFile,
289 const Utf8Str &strName,
290 const StringsList &llGroups,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 mUserData->s.llGroups = llGroups;
327
328 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
329 // the "name sync" flag determines whether the machine directory gets renamed along
330 // with the machine file; say so if the settings file name is the same as the
331 // settings file parent directory (machine directory)
332 mUserData->s.fNameSync = isInOwnDir();
333
334 // initialize the default snapshots folder
335 rc = COMSETTER(SnapshotFolder)(NULL);
336 AssertComRC(rc);
337
338 if (aOsType)
339 {
340 /* Store OS type */
341 mUserData->s.strOsType = aOsType->id();
342
343 /* Apply BIOS defaults */
344 mBIOSSettings->applyDefaults(aOsType);
345
346 /* Apply network adapters defaults */
347 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
348 mNetworkAdapters[slot]->applyDefaults(aOsType);
349
350 /* Apply serial port defaults */
351 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
352 mSerialPorts[slot]->applyDefaults(aOsType);
353 }
354
355 /* At this point the changing of the current state modification
356 * flag is allowed. */
357 allowStateModification();
358
359 /* commit all changes made during the initialization */
360 commit();
361 }
362
363 /* Confirm a successful initialization when it's the case */
364 if (SUCCEEDED(rc))
365 {
366 if (mData->mAccessible)
367 autoInitSpan.setSucceeded();
368 else
369 autoInitSpan.setLimited();
370 }
371
372 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
373 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
374 mData->mRegistered,
375 mData->mAccessible,
376 rc));
377
378 LogFlowThisFuncLeave();
379
380 return rc;
381}
382
383/**
384 * Initializes a new instance with data from machine XML (formerly Init_Registered).
385 * Gets called in two modes:
386 *
387 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
388 * UUID is specified and we mark the machine as "registered";
389 *
390 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
391 * and the machine remains unregistered until RegisterMachine() is called.
392 *
393 * @param aParent Associated parent object
394 * @param aConfigFile Local file system path to the VM settings file (can
395 * be relative to the VirtualBox config directory).
396 * @param aId UUID of the machine or NULL (see above).
397 *
398 * @return Success indicator. if not S_OK, the machine object is invalid
399 */
400HRESULT Machine::initFromSettings(VirtualBox *aParent,
401 const Utf8Str &strConfigFile,
402 const Guid *aId)
403{
404 LogFlowThisFuncEnter();
405 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
406
407 /* Enclose the state transition NotReady->InInit->Ready */
408 AutoInitSpan autoInitSpan(this);
409 AssertReturn(autoInitSpan.isOk(), E_FAIL);
410
411 HRESULT rc = initImpl(aParent, strConfigFile);
412 if (FAILED(rc)) return rc;
413
414 if (aId)
415 {
416 // loading a registered VM:
417 unconst(mData->mUuid) = *aId;
418 mData->mRegistered = TRUE;
419 // now load the settings from XML:
420 rc = registeredInit();
421 // this calls initDataAndChildObjects() and loadSettings()
422 }
423 else
424 {
425 // opening an unregistered VM (VirtualBox::OpenMachine()):
426 rc = initDataAndChildObjects();
427
428 if (SUCCEEDED(rc))
429 {
430 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
431 mData->mAccessible = TRUE;
432
433 try
434 {
435 // load and parse machine XML; this will throw on XML or logic errors
436 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
437
438 // reject VM UUID duplicates, they can happen if someone
439 // tries to register an already known VM config again
440 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
441 true /* fPermitInaccessible */,
442 false /* aDoSetError */,
443 NULL) != VBOX_E_OBJECT_NOT_FOUND)
444 {
445 throw setError(E_FAIL,
446 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
447 mData->m_strConfigFile.c_str());
448 }
449
450 // use UUID from machine config
451 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
452
453 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
454 NULL /* puuidRegistry */);
455 if (FAILED(rc)) throw rc;
456
457 /* At this point the changing of the current state modification
458 * flag is allowed. */
459 allowStateModification();
460
461 commit();
462 }
463 catch (HRESULT err)
464 {
465 /* we assume that error info is set by the thrower */
466 rc = err;
467 }
468 catch (...)
469 {
470 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
471 }
472 }
473 }
474
475 /* Confirm a successful initialization when it's the case */
476 if (SUCCEEDED(rc))
477 {
478 if (mData->mAccessible)
479 autoInitSpan.setSucceeded();
480 else
481 {
482 autoInitSpan.setLimited();
483
484 // uninit media from this machine's media registry, or else
485 // reloading the settings will fail
486 mParent->unregisterMachineMedia(getId());
487 }
488 }
489
490 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
491 "rc=%08X\n",
492 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
493 mData->mRegistered, mData->mAccessible, rc));
494
495 LogFlowThisFuncLeave();
496
497 return rc;
498}
499
500/**
501 * Initializes a new instance from a machine config that is already in memory
502 * (import OVF case). Since we are importing, the UUID in the machine
503 * config is ignored and we always generate a fresh one.
504 *
505 * @param strName Name for the new machine; this overrides what is specified in config and is used
506 * for the settings file as well.
507 * @param config Machine configuration loaded and parsed from XML.
508 *
509 * @return Success indicator. if not S_OK, the machine object is invalid
510 */
511HRESULT Machine::init(VirtualBox *aParent,
512 const Utf8Str &strName,
513 const settings::MachineConfigFile &config)
514{
515 LogFlowThisFuncEnter();
516
517 /* Enclose the state transition NotReady->InInit->Ready */
518 AutoInitSpan autoInitSpan(this);
519 AssertReturn(autoInitSpan.isOk(), E_FAIL);
520
521 Utf8Str strConfigFile;
522 aParent->getDefaultMachineFolder(strConfigFile);
523 strConfigFile.append(RTPATH_DELIMITER);
524 strConfigFile.append(strName);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(".vbox");
528
529 HRESULT rc = initImpl(aParent, strConfigFile);
530 if (FAILED(rc)) return rc;
531
532 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
533 if (FAILED(rc)) return rc;
534
535 rc = initDataAndChildObjects();
536
537 if (SUCCEEDED(rc))
538 {
539 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
540 mData->mAccessible = TRUE;
541
542 // create empty machine config for instance data
543 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
544
545 // generate fresh UUID, ignore machine config
546 unconst(mData->mUuid).create();
547
548 rc = loadMachineDataFromSettings(config,
549 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
550
551 // override VM name as well, it may be different
552 mUserData->s.strName = strName;
553
554 if (SUCCEEDED(rc))
555 {
556 /* At this point the changing of the current state modification
557 * flag is allowed. */
558 allowStateModification();
559
560 /* commit all changes made during the initialization */
561 commit();
562 }
563 }
564
565 /* Confirm a successful initialization when it's the case */
566 if (SUCCEEDED(rc))
567 {
568 if (mData->mAccessible)
569 autoInitSpan.setSucceeded();
570 else
571 {
572 autoInitSpan.setLimited();
573
574 // uninit media from this machine's media registry, or else
575 // reloading the settings will fail
576 mParent->unregisterMachineMedia(getId());
577 }
578 }
579
580 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
581 "rc=%08X\n",
582 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
583 mData->mRegistered, mData->mAccessible, rc));
584
585 LogFlowThisFuncLeave();
586
587 return rc;
588}
589
590/**
591 * Shared code between the various init() implementations.
592 * @param aParent
593 * @return
594 */
595HRESULT Machine::initImpl(VirtualBox *aParent,
596 const Utf8Str &strConfigFile)
597{
598 LogFlowThisFuncEnter();
599
600 AssertReturn(aParent, E_INVALIDARG);
601 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
602
603 HRESULT rc = S_OK;
604
605 /* share the parent weakly */
606 unconst(mParent) = aParent;
607
608 /* allocate the essential machine data structure (the rest will be
609 * allocated later by initDataAndChildObjects() */
610 mData.allocate();
611
612 /* memorize the config file name (as provided) */
613 mData->m_strConfigFile = strConfigFile;
614
615 /* get the full file name */
616 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
617 if (RT_FAILURE(vrc1))
618 return setError(VBOX_E_FILE_ERROR,
619 tr("Invalid machine settings file name '%s' (%Rrc)"),
620 strConfigFile.c_str(),
621 vrc1);
622
623 LogFlowThisFuncLeave();
624
625 return rc;
626}
627
628/**
629 * Tries to create a machine settings file in the path stored in the machine
630 * instance data. Used when a new machine is created to fail gracefully if
631 * the settings file could not be written (e.g. because machine dir is read-only).
632 * @return
633 */
634HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
635{
636 HRESULT rc = S_OK;
637
638 // when we create a new machine, we must be able to create the settings file
639 RTFILE f = NIL_RTFILE;
640 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
641 if ( RT_SUCCESS(vrc)
642 || vrc == VERR_SHARING_VIOLATION
643 )
644 {
645 if (RT_SUCCESS(vrc))
646 RTFileClose(f);
647 if (!fForceOverwrite)
648 rc = setError(VBOX_E_FILE_ERROR,
649 tr("Machine settings file '%s' already exists"),
650 mData->m_strConfigFileFull.c_str());
651 else
652 {
653 /* try to delete the config file, as otherwise the creation
654 * of a new settings file will fail. */
655 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
656 if (RT_FAILURE(vrc2))
657 rc = setError(VBOX_E_FILE_ERROR,
658 tr("Could not delete the existing settings file '%s' (%Rrc)"),
659 mData->m_strConfigFileFull.c_str(), vrc2);
660 }
661 }
662 else if ( vrc != VERR_FILE_NOT_FOUND
663 && vrc != VERR_PATH_NOT_FOUND
664 )
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Invalid machine settings file name '%s' (%Rrc)"),
667 mData->m_strConfigFileFull.c_str(),
668 vrc);
669 return rc;
670}
671
672/**
673 * Initializes the registered machine by loading the settings file.
674 * This method is separated from #init() in order to make it possible to
675 * retry the operation after VirtualBox startup instead of refusing to
676 * startup the whole VirtualBox server in case if the settings file of some
677 * registered VM is invalid or inaccessible.
678 *
679 * @note Must be always called from this object's write lock
680 * (unless called from #init() that doesn't need any locking).
681 * @note Locks the mUSBController method for writing.
682 * @note Subclasses must not call this method.
683 */
684HRESULT Machine::registeredInit()
685{
686 AssertReturn(!isSessionMachine(), E_FAIL);
687 AssertReturn(!isSnapshotMachine(), E_FAIL);
688 AssertReturn(mData->mUuid.isValid(), E_FAIL);
689 AssertReturn(!mData->mAccessible, E_FAIL);
690
691 HRESULT rc = initDataAndChildObjects();
692
693 if (SUCCEEDED(rc))
694 {
695 /* Temporarily reset the registered flag in order to let setters
696 * potentially called from loadSettings() succeed (isMutable() used in
697 * all setters will return FALSE for a Machine instance if mRegistered
698 * is TRUE). */
699 mData->mRegistered = FALSE;
700
701 try
702 {
703 // load and parse machine XML; this will throw on XML or logic errors
704 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
705
706 if (mData->mUuid != mData->pMachineConfigFile->uuid)
707 throw setError(E_FAIL,
708 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
709 mData->pMachineConfigFile->uuid.raw(),
710 mData->m_strConfigFileFull.c_str(),
711 mData->mUuid.toString().c_str(),
712 mParent->settingsFilePath().c_str());
713
714 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
715 NULL /* const Guid *puuidRegistry */);
716 if (FAILED(rc)) throw rc;
717 }
718 catch (HRESULT err)
719 {
720 /* we assume that error info is set by the thrower */
721 rc = err;
722 }
723 catch (...)
724 {
725 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
726 }
727
728 /* Restore the registered flag (even on failure) */
729 mData->mRegistered = TRUE;
730 }
731
732 if (SUCCEEDED(rc))
733 {
734 /* Set mAccessible to TRUE only if we successfully locked and loaded
735 * the settings file */
736 mData->mAccessible = TRUE;
737
738 /* commit all changes made during loading the settings file */
739 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
740 /// @todo r=klaus for some reason the settings loading logic backs up
741 // the settings, and therefore a commit is needed. Should probably be changed.
742 }
743 else
744 {
745 /* If the machine is registered, then, instead of returning a
746 * failure, we mark it as inaccessible and set the result to
747 * success to give it a try later */
748
749 /* fetch the current error info */
750 mData->mAccessError = com::ErrorInfo();
751 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
752 mData->mUuid.raw(),
753 mData->mAccessError.getText().raw()));
754
755 /* rollback all changes */
756 rollback(false /* aNotify */);
757
758 // uninit media from this machine's media registry, or else
759 // reloading the settings will fail
760 mParent->unregisterMachineMedia(getId());
761
762 /* uninitialize the common part to make sure all data is reset to
763 * default (null) values */
764 uninitDataAndChildObjects();
765
766 rc = S_OK;
767 }
768
769 return rc;
770}
771
772/**
773 * Uninitializes the instance.
774 * Called either from FinalRelease() or by the parent when it gets destroyed.
775 *
776 * @note The caller of this method must make sure that this object
777 * a) doesn't have active callers on the current thread and b) is not locked
778 * by the current thread; otherwise uninit() will hang either a) due to
779 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
780 * a dead-lock caused by this thread waiting for all callers on the other
781 * threads are done but preventing them from doing so by holding a lock.
782 */
783void Machine::uninit()
784{
785 LogFlowThisFuncEnter();
786
787 Assert(!isWriteLockOnCurrentThread());
788
789 Assert(!uRegistryNeedsSaving);
790 if (uRegistryNeedsSaving)
791 {
792 AutoCaller autoCaller(this);
793 if (SUCCEEDED(autoCaller.rc()))
794 {
795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
796 saveSettings(NULL, Machine::SaveS_Force);
797 }
798 }
799
800 /* Enclose the state transition Ready->InUninit->NotReady */
801 AutoUninitSpan autoUninitSpan(this);
802 if (autoUninitSpan.uninitDone())
803 return;
804
805 Assert(!isSnapshotMachine());
806 Assert(!isSessionMachine());
807 Assert(!!mData);
808
809 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
810 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
811
812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
813
814 if (!mData->mSession.mMachine.isNull())
815 {
816 /* Theoretically, this can only happen if the VirtualBox server has been
817 * terminated while there were clients running that owned open direct
818 * sessions. Since in this case we are definitely called by
819 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
820 * won't happen on the client watcher thread (because it does
821 * VirtualBox::addCaller() for the duration of the
822 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
823 * cannot happen until the VirtualBox caller is released). This is
824 * important, because SessionMachine::uninit() cannot correctly operate
825 * after we return from this method (it expects the Machine instance is
826 * still valid). We'll call it ourselves below.
827 */
828 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
829 (SessionMachine*)mData->mSession.mMachine));
830
831 if (Global::IsOnlineOrTransient(mData->mMachineState))
832 {
833 LogWarningThisFunc(("Setting state to Aborted!\n"));
834 /* set machine state using SessionMachine reimplementation */
835 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
836 }
837
838 /*
839 * Uninitialize SessionMachine using public uninit() to indicate
840 * an unexpected uninitialization.
841 */
842 mData->mSession.mMachine->uninit();
843 /* SessionMachine::uninit() must set mSession.mMachine to null */
844 Assert(mData->mSession.mMachine.isNull());
845 }
846
847 // uninit media from this machine's media registry, if they're still there
848 Guid uuidMachine(getId());
849
850 /* the lock is no more necessary (SessionMachine is uninitialized) */
851 alock.release();
852
853 /* XXX This will fail with
854 * "cannot be closed because it is still attached to 1 virtual machines"
855 * because at this point we did not call uninitDataAndChildObjects() yet
856 * and therefore also removeBackReference() for all these mediums was not called! */
857
858 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
859 mParent->unregisterMachineMedia(uuidMachine);
860
861 // has machine been modified?
862 if (mData->flModifications)
863 {
864 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
865 rollback(false /* aNotify */);
866 }
867
868 if (mData->mAccessible)
869 uninitDataAndChildObjects();
870
871 /* free the essential data structure last */
872 mData.free();
873
874 LogFlowThisFuncLeave();
875}
876
877// IMachine properties
878/////////////////////////////////////////////////////////////////////////////
879
880STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
881{
882 CheckComArgOutPointerValid(aParent);
883
884 AutoLimitedCaller autoCaller(this);
885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
886
887 /* mParent is constant during life time, no need to lock */
888 ComObjPtr<VirtualBox> pVirtualBox(mParent);
889 pVirtualBox.queryInterfaceTo(aParent);
890
891 return S_OK;
892}
893
894STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
895{
896 CheckComArgOutPointerValid(aAccessible);
897
898 AutoLimitedCaller autoCaller(this);
899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
900
901 LogFlowThisFunc(("ENTER\n"));
902
903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
904
905 HRESULT rc = S_OK;
906
907 if (!mData->mAccessible)
908 {
909 /* try to initialize the VM once more if not accessible */
910
911 AutoReinitSpan autoReinitSpan(this);
912 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
913
914#ifdef DEBUG
915 LogFlowThisFunc(("Dumping media backreferences\n"));
916 mParent->dumpAllBackRefs();
917#endif
918
919 if (mData->pMachineConfigFile)
920 {
921 // reset the XML file to force loadSettings() (called from registeredInit())
922 // to parse it again; the file might have changed
923 delete mData->pMachineConfigFile;
924 mData->pMachineConfigFile = NULL;
925 }
926
927 rc = registeredInit();
928
929 if (SUCCEEDED(rc) && mData->mAccessible)
930 {
931 autoReinitSpan.setSucceeded();
932
933 /* make sure interesting parties will notice the accessibility
934 * state change */
935 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
936 mParent->onMachineDataChange(mData->mUuid);
937 }
938 }
939
940 if (SUCCEEDED(rc))
941 *aAccessible = mData->mAccessible;
942
943 LogFlowThisFuncLeave();
944
945 return rc;
946}
947
948STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
949{
950 CheckComArgOutPointerValid(aAccessError);
951
952 AutoLimitedCaller autoCaller(this);
953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
954
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 rc = errorInfo.queryInterfaceTo(aAccessError);
975 }
976
977 return rc;
978}
979
980STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
981{
982 CheckComArgOutPointerValid(aName);
983
984 AutoCaller autoCaller(this);
985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
988
989 mUserData->s.strName.cloneTo(aName);
990
991 return S_OK;
992}
993
994STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
995{
996 CheckComArgStrNotEmptyOrNull(aName);
997
998 AutoCaller autoCaller(this);
999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1000
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1021{
1022 CheckComArgOutPointerValid(aDescription);
1023
1024 AutoCaller autoCaller(this);
1025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1026
1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 mUserData->s.strDescription.cloneTo(aDescription);
1030
1031 return S_OK;
1032}
1033
1034STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1035{
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 // this can be done in principle in any state as it doesn't affect the VM
1042 // significantly, but play safe by not messing around while complex
1043 // activities are going on
1044 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1045 if (FAILED(rc)) return rc;
1046
1047 setModified(IsModified_MachineData);
1048 mUserData.backup();
1049 mUserData->s.strDescription = aDescription;
1050
1051 return S_OK;
1052}
1053
1054STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1055{
1056 CheckComArgOutPointerValid(aId);
1057
1058 AutoLimitedCaller autoCaller(this);
1059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1060
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 mData->mUuid.toUtf16().cloneTo(aId);
1064
1065 return S_OK;
1066}
1067
1068STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1069{
1070 CheckComArgOutSafeArrayPointerValid(aGroups);
1071
1072 AutoCaller autoCaller(this);
1073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1074
1075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1076 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1077 size_t i = 0;
1078 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1079 it != mUserData->s.llGroups.end();
1080 ++it, i++)
1081 {
1082 Bstr tmp = *it;
1083 tmp.cloneTo(&groups[i]);
1084 }
1085 groups.detachTo(ComSafeArrayOutArg(aGroups));
1086
1087 return S_OK;
1088}
1089
1090STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1091{
1092 AutoCaller autoCaller(this);
1093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1094
1095 StringsList llGroups;
1096 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1097 if (FAILED(rc))
1098 return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 // changing machine groups is possible while the VM is offline
1103 rc = checkStateDependency(OfflineStateDep);
1104 if (FAILED(rc)) return rc;
1105
1106 setModified(IsModified_MachineData);
1107 mUserData.backup();
1108 mUserData->s.llGroups = llGroups;
1109
1110 return S_OK;
1111}
1112
1113STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1114{
1115 CheckComArgOutPointerValid(aOSTypeId);
1116
1117 AutoCaller autoCaller(this);
1118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1119
1120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 mUserData->s.strOsType.cloneTo(aOSTypeId);
1123
1124 return S_OK;
1125}
1126
1127STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1128{
1129 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1130
1131 AutoCaller autoCaller(this);
1132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1133
1134 /* look up the object by Id to check it is valid */
1135 ComPtr<IGuestOSType> guestOSType;
1136 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1137 if (FAILED(rc)) return rc;
1138
1139 /* when setting, always use the "etalon" value for consistency -- lookup
1140 * by ID is case-insensitive and the input value may have different case */
1141 Bstr osTypeId;
1142 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 rc = checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 setModified(IsModified_MachineData);
1151 mUserData.backup();
1152 mUserData->s.strOsType = osTypeId;
1153
1154 return S_OK;
1155}
1156
1157
1158STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1159{
1160 CheckComArgOutPointerValid(aFirmwareType);
1161
1162 AutoCaller autoCaller(this);
1163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1164
1165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 *aFirmwareType = mHWData->mFirmwareType;
1168
1169 return S_OK;
1170}
1171
1172STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1173{
1174 AutoCaller autoCaller(this);
1175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mFirmwareType = aFirmwareType;
1184
1185 return S_OK;
1186}
1187
1188STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1189{
1190 CheckComArgOutPointerValid(aKeyboardHIDType);
1191
1192 AutoCaller autoCaller(this);
1193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1194
1195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1196
1197 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1198
1199 return S_OK;
1200}
1201
1202STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1203{
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 HRESULT rc = checkStateDependency(MutableStateDep);
1209 if (FAILED(rc)) return rc;
1210
1211 setModified(IsModified_MachineData);
1212 mHWData.backup();
1213 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1214
1215 return S_OK;
1216}
1217
1218STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1219{
1220 CheckComArgOutPointerValid(aPointingHIDType);
1221
1222 AutoCaller autoCaller(this);
1223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1224
1225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1226
1227 *aPointingHIDType = mHWData->mPointingHIDType;
1228
1229 return S_OK;
1230}
1231
1232STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1233{
1234 AutoCaller autoCaller(this);
1235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1237
1238 HRESULT rc = checkStateDependency(MutableStateDep);
1239 if (FAILED(rc)) return rc;
1240
1241 setModified(IsModified_MachineData);
1242 mHWData.backup();
1243 mHWData->mPointingHIDType = aPointingHIDType;
1244
1245 return S_OK;
1246}
1247
1248STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1249{
1250 CheckComArgOutPointerValid(aChipsetType);
1251
1252 AutoCaller autoCaller(this);
1253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1254
1255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 *aChipsetType = mHWData->mChipsetType;
1258
1259 return S_OK;
1260}
1261
1262STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1263{
1264 AutoCaller autoCaller(this);
1265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aChipsetType != mHWData->mChipsetType)
1272 {
1273 setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mChipsetType = aChipsetType;
1276
1277 // Resize network adapter array, to be finalized on commit/rollback.
1278 // We must not throw away entries yet, otherwise settings are lost
1279 // without a way to roll back.
1280 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1281 uint32_t oldCount = mNetworkAdapters.size();
1282 if (newCount > oldCount)
1283 {
1284 mNetworkAdapters.resize(newCount);
1285 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1286 {
1287 unconst(mNetworkAdapters[slot]).createObject();
1288 mNetworkAdapters[slot]->init(this, slot);
1289 }
1290 }
1291 }
1292
1293 return S_OK;
1294}
1295
1296STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1297{
1298 CheckComArgOutPointerValid(aHWVersion);
1299
1300 AutoCaller autoCaller(this);
1301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1302
1303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1304
1305 mHWData->mHWVersion.cloneTo(aHWVersion);
1306
1307 return S_OK;
1308}
1309
1310STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1311{
1312 /* check known version */
1313 Utf8Str hwVersion = aHWVersion;
1314 if ( hwVersion.compare("1") != 0
1315 && hwVersion.compare("2") != 0)
1316 return setError(E_INVALIDARG,
1317 tr("Invalid hardware version: %ls\n"), aHWVersion);
1318
1319 AutoCaller autoCaller(this);
1320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1321
1322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1323
1324 HRESULT rc = checkStateDependency(MutableStateDep);
1325 if (FAILED(rc)) return rc;
1326
1327 setModified(IsModified_MachineData);
1328 mHWData.backup();
1329 mHWData->mHWVersion = hwVersion;
1330
1331 return S_OK;
1332}
1333
1334STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1335{
1336 CheckComArgOutPointerValid(aUUID);
1337
1338 AutoCaller autoCaller(this);
1339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1340
1341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1342
1343 if (mHWData->mHardwareUUID.isValid())
1344 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1345 else
1346 mData->mUuid.toUtf16().cloneTo(aUUID);
1347
1348 return S_OK;
1349}
1350
1351STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1352{
1353 Guid hardwareUUID(aUUID);
1354 if (!hardwareUUID.isValid())
1355 return E_INVALIDARG;
1356
1357 AutoCaller autoCaller(this);
1358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1359
1360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1361
1362 HRESULT rc = checkStateDependency(MutableStateDep);
1363 if (FAILED(rc)) return rc;
1364
1365 setModified(IsModified_MachineData);
1366 mHWData.backup();
1367 if (hardwareUUID == mData->mUuid)
1368 mHWData->mHardwareUUID.clear();
1369 else
1370 mHWData->mHardwareUUID = hardwareUUID;
1371
1372 return S_OK;
1373}
1374
1375STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1376{
1377 CheckComArgOutPointerValid(memorySize);
1378
1379 AutoCaller autoCaller(this);
1380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1381
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 *memorySize = mHWData->mMemorySize;
1385
1386 return S_OK;
1387}
1388
1389STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1390{
1391 /* check RAM limits */
1392 if ( memorySize < MM_RAM_MIN_IN_MB
1393 || memorySize > MM_RAM_MAX_IN_MB
1394 )
1395 return setError(E_INVALIDARG,
1396 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1397 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1398
1399 AutoCaller autoCaller(this);
1400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1401
1402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1403
1404 HRESULT rc = checkStateDependency(MutableStateDep);
1405 if (FAILED(rc)) return rc;
1406
1407 setModified(IsModified_MachineData);
1408 mHWData.backup();
1409 mHWData->mMemorySize = memorySize;
1410
1411 return S_OK;
1412}
1413
1414STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1415{
1416 CheckComArgOutPointerValid(CPUCount);
1417
1418 AutoCaller autoCaller(this);
1419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1420
1421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 *CPUCount = mHWData->mCPUCount;
1424
1425 return S_OK;
1426}
1427
1428STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1429{
1430 /* check CPU limits */
1431 if ( CPUCount < SchemaDefs::MinCPUCount
1432 || CPUCount > SchemaDefs::MaxCPUCount
1433 )
1434 return setError(E_INVALIDARG,
1435 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1436 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1437
1438 AutoCaller autoCaller(this);
1439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1440
1441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1442
1443 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1444 if (mHWData->mCPUHotPlugEnabled)
1445 {
1446 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1447 {
1448 if (mHWData->mCPUAttached[idx])
1449 return setError(E_INVALIDARG,
1450 tr("There is still a CPU attached to socket %lu."
1451 "Detach the CPU before removing the socket"),
1452 CPUCount, idx+1);
1453 }
1454 }
1455
1456 HRESULT rc = checkStateDependency(MutableStateDep);
1457 if (FAILED(rc)) return rc;
1458
1459 setModified(IsModified_MachineData);
1460 mHWData.backup();
1461 mHWData->mCPUCount = CPUCount;
1462
1463 return S_OK;
1464}
1465
1466STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1467{
1468 CheckComArgOutPointerValid(aExecutionCap);
1469
1470 AutoCaller autoCaller(this);
1471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1472
1473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1474
1475 *aExecutionCap = mHWData->mCpuExecutionCap;
1476
1477 return S_OK;
1478}
1479
1480STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1481{
1482 HRESULT rc = S_OK;
1483
1484 /* check throttle limits */
1485 if ( aExecutionCap < 1
1486 || aExecutionCap > 100
1487 )
1488 return setError(E_INVALIDARG,
1489 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1490 aExecutionCap, 1, 100);
1491
1492 AutoCaller autoCaller(this);
1493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1494
1495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
1497 alock.release();
1498 rc = onCPUExecutionCapChange(aExecutionCap);
1499 alock.acquire();
1500 if (FAILED(rc)) return rc;
1501
1502 setModified(IsModified_MachineData);
1503 mHWData.backup();
1504 mHWData->mCpuExecutionCap = aExecutionCap;
1505
1506 /* Save settings if online - todo why is this required?? */
1507 if (Global::IsOnline(mData->mMachineState))
1508 saveSettings(NULL);
1509
1510 return S_OK;
1511}
1512
1513
1514STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1515{
1516 CheckComArgOutPointerValid(enabled);
1517
1518 AutoCaller autoCaller(this);
1519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1520
1521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1522
1523 *enabled = mHWData->mCPUHotPlugEnabled;
1524
1525 return S_OK;
1526}
1527
1528STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1529{
1530 HRESULT rc = S_OK;
1531
1532 AutoCaller autoCaller(this);
1533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1534
1535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 rc = checkStateDependency(MutableStateDep);
1538 if (FAILED(rc)) return rc;
1539
1540 if (mHWData->mCPUHotPlugEnabled != enabled)
1541 {
1542 if (enabled)
1543 {
1544 setModified(IsModified_MachineData);
1545 mHWData.backup();
1546
1547 /* Add the amount of CPUs currently attached */
1548 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1549 {
1550 mHWData->mCPUAttached[i] = true;
1551 }
1552 }
1553 else
1554 {
1555 /*
1556 * We can disable hotplug only if the amount of maximum CPUs is equal
1557 * to the amount of attached CPUs
1558 */
1559 unsigned cCpusAttached = 0;
1560 unsigned iHighestId = 0;
1561
1562 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1563 {
1564 if (mHWData->mCPUAttached[i])
1565 {
1566 cCpusAttached++;
1567 iHighestId = i;
1568 }
1569 }
1570
1571 if ( (cCpusAttached != mHWData->mCPUCount)
1572 || (iHighestId >= mHWData->mCPUCount))
1573 return setError(E_INVALIDARG,
1574 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1575
1576 setModified(IsModified_MachineData);
1577 mHWData.backup();
1578 }
1579 }
1580
1581 mHWData->mCPUHotPlugEnabled = enabled;
1582
1583 return rc;
1584}
1585
1586STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1587{
1588#ifdef VBOX_WITH_USB_CARDREADER
1589 CheckComArgOutPointerValid(enabled);
1590
1591 AutoCaller autoCaller(this);
1592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1593
1594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1597
1598 return S_OK;
1599#else
1600 NOREF(enabled);
1601 return E_NOTIMPL;
1602#endif
1603}
1604
1605STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1606{
1607#ifdef VBOX_WITH_USB_CARDREADER
1608 AutoCaller autoCaller(this);
1609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1611
1612 HRESULT rc = checkStateDependency(MutableStateDep);
1613 if (FAILED(rc)) return rc;
1614
1615 setModified(IsModified_MachineData);
1616 mHWData.backup();
1617 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1618
1619 return S_OK;
1620#else
1621 NOREF(enabled);
1622 return E_NOTIMPL;
1623#endif
1624}
1625
1626STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1627{
1628#ifdef VBOX_WITH_USB_VIDEO
1629 CheckComArgOutPointerValid(enabled);
1630
1631 AutoCaller autoCaller(this);
1632 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1633
1634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 *enabled = mHWData->mEmulatedUSBWebcamEnabled;
1637
1638 return S_OK;
1639#else
1640 NOREF(enabled);
1641 return E_NOTIMPL;
1642#endif
1643}
1644
1645STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1646{
1647#ifdef VBOX_WITH_USB_VIDEO
1648 AutoCaller autoCaller(this);
1649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1651
1652 HRESULT rc = checkStateDependency(MutableStateDep);
1653 if (FAILED(rc)) return rc;
1654
1655 setModified(IsModified_MachineData);
1656 mHWData.backup();
1657 mHWData->mEmulatedUSBWebcamEnabled = enabled;
1658
1659 return S_OK;
1660#else
1661 NOREF(enabled);
1662 return E_NOTIMPL;
1663#endif
1664}
1665
1666STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1667{
1668 CheckComArgOutPointerValid(enabled);
1669
1670 AutoCaller autoCaller(this);
1671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *enabled = mHWData->mHPETEnabled;
1675
1676 return S_OK;
1677}
1678
1679STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1680{
1681 HRESULT rc = S_OK;
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 rc = checkStateDependency(MutableStateDep);
1688 if (FAILED(rc)) return rc;
1689
1690 setModified(IsModified_MachineData);
1691 mHWData.backup();
1692
1693 mHWData->mHPETEnabled = enabled;
1694
1695 return rc;
1696}
1697
1698STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1699{
1700 AutoCaller autoCaller(this);
1701 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1702
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 *fEnabled = mHWData->mVideoCaptureEnabled;
1706 return S_OK;
1707}
1708
1709STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1710{
1711 AutoCaller autoCaller(this);
1712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1713
1714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1715 mHWData->mVideoCaptureEnabled = fEnabled;
1716 return S_OK;
1717}
1718
1719STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1720{
1721 AutoCaller autoCaller(this);
1722 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1723
1724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1725 mHWData->mVideoCaptureFile.cloneTo(apFile);
1726 return S_OK;
1727}
1728
1729STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1730{
1731 Utf8Str strFile(aFile);
1732 AutoCaller autoCaller(this);
1733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1734
1735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1736 if (strFile.isEmpty())
1737 strFile = "VideoCap.webm";
1738 mHWData->mVideoCaptureFile = strFile;
1739 return S_OK;
1740}
1741
1742
1743STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1744{
1745 AutoCaller autoCaller(this);
1746 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1747
1748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1749 *ulHorzRes = mHWData->mVideoCaptureWidth;
1750 return S_OK;
1751}
1752
1753STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1754{
1755 AutoCaller autoCaller(this);
1756 if (FAILED(autoCaller.rc()))
1757 {
1758 LogFlow(("Autolocked failed\n"));
1759 return autoCaller.rc();
1760 }
1761
1762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 mHWData->mVideoCaptureWidth = ulHorzRes;
1764 return S_OK;
1765}
1766
1767STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1768{
1769 AutoCaller autoCaller(this);
1770 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1771
1772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1773 *ulVertRes = mHWData->mVideoCaptureHeight;
1774 return S_OK;
1775}
1776
1777STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1778{
1779 AutoCaller autoCaller(this);
1780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1781
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783 mHWData->mVideoCaptureHeight = ulVertRes;
1784 return S_OK;
1785}
1786
1787STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1788{
1789 CheckComArgOutPointerValid(memorySize);
1790
1791 AutoCaller autoCaller(this);
1792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1793
1794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 *memorySize = mHWData->mVRAMSize;
1797
1798 return S_OK;
1799}
1800
1801STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1802{
1803 /* check VRAM limits */
1804 if (memorySize < SchemaDefs::MinGuestVRAM ||
1805 memorySize > SchemaDefs::MaxGuestVRAM)
1806 return setError(E_INVALIDARG,
1807 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1808 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1809
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 HRESULT rc = checkStateDependency(MutableStateDep);
1816 if (FAILED(rc)) return rc;
1817
1818 setModified(IsModified_MachineData);
1819 mHWData.backup();
1820 mHWData->mVRAMSize = memorySize;
1821
1822 return S_OK;
1823}
1824
1825/** @todo this method should not be public */
1826STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1827{
1828 CheckComArgOutPointerValid(memoryBalloonSize);
1829
1830 AutoCaller autoCaller(this);
1831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1832
1833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1834
1835 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1836
1837 return S_OK;
1838}
1839
1840/**
1841 * Set the memory balloon size.
1842 *
1843 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1844 * we have to make sure that we never call IGuest from here.
1845 */
1846STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1847{
1848 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1849#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1850 /* check limits */
1851 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1852 return setError(E_INVALIDARG,
1853 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1854 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1855
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 setModified(IsModified_MachineData);
1862 mHWData.backup();
1863 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1864
1865 return S_OK;
1866#else
1867 NOREF(memoryBalloonSize);
1868 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1869#endif
1870}
1871
1872STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1873{
1874 CheckComArgOutPointerValid(enabled);
1875
1876 AutoCaller autoCaller(this);
1877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1878
1879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 *enabled = mHWData->mPageFusionEnabled;
1882 return S_OK;
1883}
1884
1885STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1886{
1887#ifdef VBOX_WITH_PAGE_SHARING
1888 AutoCaller autoCaller(this);
1889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1890
1891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1894 setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mPageFusionEnabled = enabled;
1897 return S_OK;
1898#else
1899 NOREF(enabled);
1900 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1901#endif
1902}
1903
1904STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1905{
1906 CheckComArgOutPointerValid(enabled);
1907
1908 AutoCaller autoCaller(this);
1909 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1910
1911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1912
1913 *enabled = mHWData->mAccelerate3DEnabled;
1914
1915 return S_OK;
1916}
1917
1918STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1919{
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 HRESULT rc = checkStateDependency(MutableStateDep);
1926 if (FAILED(rc)) return rc;
1927
1928 /** @todo check validity! */
1929
1930 setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mAccelerate3DEnabled = enable;
1933
1934 return S_OK;
1935}
1936
1937
1938STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1939{
1940 CheckComArgOutPointerValid(enabled);
1941
1942 AutoCaller autoCaller(this);
1943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1944
1945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1946
1947 *enabled = mHWData->mAccelerate2DVideoEnabled;
1948
1949 return S_OK;
1950}
1951
1952STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1953{
1954 AutoCaller autoCaller(this);
1955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1956
1957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 HRESULT rc = checkStateDependency(MutableStateDep);
1960 if (FAILED(rc)) return rc;
1961
1962 /** @todo check validity! */
1963
1964 setModified(IsModified_MachineData);
1965 mHWData.backup();
1966 mHWData->mAccelerate2DVideoEnabled = enable;
1967
1968 return S_OK;
1969}
1970
1971STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1972{
1973 CheckComArgOutPointerValid(monitorCount);
1974
1975 AutoCaller autoCaller(this);
1976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1977
1978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1979
1980 *monitorCount = mHWData->mMonitorCount;
1981
1982 return S_OK;
1983}
1984
1985STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1986{
1987 /* make sure monitor count is a sensible number */
1988 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1989 return setError(E_INVALIDARG,
1990 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1991 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1992
1993 AutoCaller autoCaller(this);
1994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1995
1996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 HRESULT rc = checkStateDependency(MutableStateDep);
1999 if (FAILED(rc)) return rc;
2000
2001 setModified(IsModified_MachineData);
2002 mHWData.backup();
2003 mHWData->mMonitorCount = monitorCount;
2004
2005 return S_OK;
2006}
2007
2008STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2009{
2010 CheckComArgOutPointerValid(biosSettings);
2011
2012 AutoCaller autoCaller(this);
2013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2014
2015 /* mBIOSSettings is constant during life time, no need to lock */
2016 mBIOSSettings.queryInterfaceTo(biosSettings);
2017
2018 return S_OK;
2019}
2020
2021STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2022{
2023 CheckComArgOutPointerValid(aVal);
2024
2025 AutoCaller autoCaller(this);
2026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2027
2028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 switch(property)
2031 {
2032 case CPUPropertyType_PAE:
2033 *aVal = mHWData->mPAEEnabled;
2034 break;
2035
2036 case CPUPropertyType_Synthetic:
2037 *aVal = mHWData->mSyntheticCpu;
2038 break;
2039
2040 default:
2041 return E_INVALIDARG;
2042 }
2043 return S_OK;
2044}
2045
2046STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2047{
2048 AutoCaller autoCaller(this);
2049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2050
2051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2052
2053 HRESULT rc = checkStateDependency(MutableStateDep);
2054 if (FAILED(rc)) return rc;
2055
2056 switch(property)
2057 {
2058 case CPUPropertyType_PAE:
2059 setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mPAEEnabled = !!aVal;
2062 break;
2063
2064 case CPUPropertyType_Synthetic:
2065 setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mSyntheticCpu = !!aVal;
2068 break;
2069
2070 default:
2071 return E_INVALIDARG;
2072 }
2073 return S_OK;
2074}
2075
2076STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2077{
2078 CheckComArgOutPointerValid(aValEax);
2079 CheckComArgOutPointerValid(aValEbx);
2080 CheckComArgOutPointerValid(aValEcx);
2081 CheckComArgOutPointerValid(aValEdx);
2082
2083 AutoCaller autoCaller(this);
2084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2085
2086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2087
2088 switch(aId)
2089 {
2090 case 0x0:
2091 case 0x1:
2092 case 0x2:
2093 case 0x3:
2094 case 0x4:
2095 case 0x5:
2096 case 0x6:
2097 case 0x7:
2098 case 0x8:
2099 case 0x9:
2100 case 0xA:
2101 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2102 return E_INVALIDARG;
2103
2104 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2105 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2106 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2107 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2108 break;
2109
2110 case 0x80000000:
2111 case 0x80000001:
2112 case 0x80000002:
2113 case 0x80000003:
2114 case 0x80000004:
2115 case 0x80000005:
2116 case 0x80000006:
2117 case 0x80000007:
2118 case 0x80000008:
2119 case 0x80000009:
2120 case 0x8000000A:
2121 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2122 return E_INVALIDARG;
2123
2124 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2125 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2126 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2127 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2128 break;
2129
2130 default:
2131 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2132 }
2133 return S_OK;
2134}
2135
2136STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2137{
2138 AutoCaller autoCaller(this);
2139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2140
2141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2142
2143 HRESULT rc = checkStateDependency(MutableStateDep);
2144 if (FAILED(rc)) return rc;
2145
2146 switch(aId)
2147 {
2148 case 0x0:
2149 case 0x1:
2150 case 0x2:
2151 case 0x3:
2152 case 0x4:
2153 case 0x5:
2154 case 0x6:
2155 case 0x7:
2156 case 0x8:
2157 case 0x9:
2158 case 0xA:
2159 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2160 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2161 setModified(IsModified_MachineData);
2162 mHWData.backup();
2163 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2164 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2165 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2166 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2167 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2168 break;
2169
2170 case 0x80000000:
2171 case 0x80000001:
2172 case 0x80000002:
2173 case 0x80000003:
2174 case 0x80000004:
2175 case 0x80000005:
2176 case 0x80000006:
2177 case 0x80000007:
2178 case 0x80000008:
2179 case 0x80000009:
2180 case 0x8000000A:
2181 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2182 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2183 setModified(IsModified_MachineData);
2184 mHWData.backup();
2185 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2186 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2187 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2188 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2189 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2190 break;
2191
2192 default:
2193 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2194 }
2195 return S_OK;
2196}
2197
2198STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2199{
2200 AutoCaller autoCaller(this);
2201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2202
2203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2204
2205 HRESULT rc = checkStateDependency(MutableStateDep);
2206 if (FAILED(rc)) return rc;
2207
2208 switch(aId)
2209 {
2210 case 0x0:
2211 case 0x1:
2212 case 0x2:
2213 case 0x3:
2214 case 0x4:
2215 case 0x5:
2216 case 0x6:
2217 case 0x7:
2218 case 0x8:
2219 case 0x9:
2220 case 0xA:
2221 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2222 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2223 setModified(IsModified_MachineData);
2224 mHWData.backup();
2225 /* Invalidate leaf. */
2226 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2227 break;
2228
2229 case 0x80000000:
2230 case 0x80000001:
2231 case 0x80000002:
2232 case 0x80000003:
2233 case 0x80000004:
2234 case 0x80000005:
2235 case 0x80000006:
2236 case 0x80000007:
2237 case 0x80000008:
2238 case 0x80000009:
2239 case 0x8000000A:
2240 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2241 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2242 setModified(IsModified_MachineData);
2243 mHWData.backup();
2244 /* Invalidate leaf. */
2245 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2246 break;
2247
2248 default:
2249 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2250 }
2251 return S_OK;
2252}
2253
2254STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2255{
2256 AutoCaller autoCaller(this);
2257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2258
2259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2260
2261 HRESULT rc = checkStateDependency(MutableStateDep);
2262 if (FAILED(rc)) return rc;
2263
2264 setModified(IsModified_MachineData);
2265 mHWData.backup();
2266
2267 /* Invalidate all standard leafs. */
2268 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2269 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2270
2271 /* Invalidate all extended leafs. */
2272 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2273 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2274
2275 return S_OK;
2276}
2277
2278STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2279{
2280 CheckComArgOutPointerValid(aVal);
2281
2282 AutoCaller autoCaller(this);
2283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2284
2285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2286
2287 switch(property)
2288 {
2289 case HWVirtExPropertyType_Enabled:
2290 *aVal = mHWData->mHWVirtExEnabled;
2291 break;
2292
2293 case HWVirtExPropertyType_Exclusive:
2294 *aVal = mHWData->mHWVirtExExclusive;
2295 break;
2296
2297 case HWVirtExPropertyType_VPID:
2298 *aVal = mHWData->mHWVirtExVPIDEnabled;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2303 break;
2304
2305 case HWVirtExPropertyType_LargePages:
2306 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2307#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2308 *aVal = FALSE;
2309#endif
2310 break;
2311
2312 case HWVirtExPropertyType_Force:
2313 *aVal = mHWData->mHWVirtExForceEnabled;
2314 break;
2315
2316 default:
2317 return E_INVALIDARG;
2318 }
2319 return S_OK;
2320}
2321
2322STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2323{
2324 AutoCaller autoCaller(this);
2325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2326
2327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
2329 HRESULT rc = checkStateDependency(MutableStateDep);
2330 if (FAILED(rc)) return rc;
2331
2332 switch(property)
2333 {
2334 case HWVirtExPropertyType_Enabled:
2335 setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mHWVirtExEnabled = !!aVal;
2338 break;
2339
2340 case HWVirtExPropertyType_Exclusive:
2341 setModified(IsModified_MachineData);
2342 mHWData.backup();
2343 mHWData->mHWVirtExExclusive = !!aVal;
2344 break;
2345
2346 case HWVirtExPropertyType_VPID:
2347 setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2350 break;
2351
2352 case HWVirtExPropertyType_NestedPaging:
2353 setModified(IsModified_MachineData);
2354 mHWData.backup();
2355 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2356 break;
2357
2358 case HWVirtExPropertyType_LargePages:
2359 setModified(IsModified_MachineData);
2360 mHWData.backup();
2361 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2362 break;
2363
2364 case HWVirtExPropertyType_Force:
2365 setModified(IsModified_MachineData);
2366 mHWData.backup();
2367 mHWData->mHWVirtExForceEnabled = !!aVal;
2368 break;
2369
2370 default:
2371 return E_INVALIDARG;
2372 }
2373
2374 return S_OK;
2375}
2376
2377STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2378{
2379 CheckComArgOutPointerValid(aSnapshotFolder);
2380
2381 AutoCaller autoCaller(this);
2382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2383
2384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2385
2386 Utf8Str strFullSnapshotFolder;
2387 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2388 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2389
2390 return S_OK;
2391}
2392
2393STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2394{
2395 /* @todo (r=dmik):
2396 * 1. Allow to change the name of the snapshot folder containing snapshots
2397 * 2. Rename the folder on disk instead of just changing the property
2398 * value (to be smart and not to leave garbage). Note that it cannot be
2399 * done here because the change may be rolled back. Thus, the right
2400 * place is #saveSettings().
2401 */
2402
2403 AutoCaller autoCaller(this);
2404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2405
2406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2407
2408 HRESULT rc = checkStateDependency(MutableStateDep);
2409 if (FAILED(rc)) return rc;
2410
2411 if (!mData->mCurrentSnapshot.isNull())
2412 return setError(E_FAIL,
2413 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2414
2415 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2416
2417 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2418 if (strSnapshotFolder.isEmpty())
2419 strSnapshotFolder = "Snapshots";
2420 int vrc = calculateFullPath(strSnapshotFolder,
2421 strSnapshotFolder);
2422 if (RT_FAILURE(vrc))
2423 return setError(E_FAIL,
2424 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2425 aSnapshotFolder, vrc);
2426
2427 setModified(IsModified_MachineData);
2428 mUserData.backup();
2429
2430 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2431
2432 return S_OK;
2433}
2434
2435STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2436{
2437 CheckComArgOutSafeArrayPointerValid(aAttachments);
2438
2439 AutoCaller autoCaller(this);
2440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2441
2442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2443
2444 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2445 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2446
2447 return S_OK;
2448}
2449
2450STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2451{
2452 CheckComArgOutPointerValid(vrdeServer);
2453
2454 AutoCaller autoCaller(this);
2455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2456
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 Assert(!!mVRDEServer);
2460 mVRDEServer.queryInterfaceTo(vrdeServer);
2461
2462 return S_OK;
2463}
2464
2465STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2466{
2467 CheckComArgOutPointerValid(audioAdapter);
2468
2469 AutoCaller autoCaller(this);
2470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2471
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 mAudioAdapter.queryInterfaceTo(audioAdapter);
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2479{
2480#ifdef VBOX_WITH_VUSB
2481 CheckComArgOutPointerValid(aUSBController);
2482
2483 AutoCaller autoCaller(this);
2484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2485
2486 clearError();
2487 MultiResult rc(S_OK);
2488
2489# ifdef VBOX_WITH_USB
2490 rc = mParent->host()->checkUSBProxyService();
2491 if (FAILED(rc)) return rc;
2492# endif
2493
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 return rc = mUSBController.queryInterfaceTo(aUSBController);
2497#else
2498 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2499 * extended error info to indicate that USB is simply not available
2500 * (w/o treating it as a failure), for example, as in OSE */
2501 NOREF(aUSBController);
2502 ReturnComNotImplemented();
2503#endif /* VBOX_WITH_VUSB */
2504}
2505
2506STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2507{
2508 CheckComArgOutPointerValid(aFilePath);
2509
2510 AutoLimitedCaller autoCaller(this);
2511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2512
2513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 mData->m_strConfigFileFull.cloneTo(aFilePath);
2516 return S_OK;
2517}
2518
2519STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2520{
2521 CheckComArgOutPointerValid(aModified);
2522
2523 AutoCaller autoCaller(this);
2524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2525
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 HRESULT rc = checkStateDependency(MutableStateDep);
2529 if (FAILED(rc)) return rc;
2530
2531 if (!mData->pMachineConfigFile->fileExists())
2532 // this is a new machine, and no config file exists yet:
2533 *aModified = TRUE;
2534 else
2535 *aModified = (mData->flModifications != 0);
2536
2537 return S_OK;
2538}
2539
2540STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2541{
2542 CheckComArgOutPointerValid(aSessionState);
2543
2544 AutoCaller autoCaller(this);
2545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2546
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 *aSessionState = mData->mSession.mState;
2550
2551 return S_OK;
2552}
2553
2554STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2555{
2556 CheckComArgOutPointerValid(aSessionType);
2557
2558 AutoCaller autoCaller(this);
2559 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2560
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 mData->mSession.mType.cloneTo(aSessionType);
2564
2565 return S_OK;
2566}
2567
2568STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2569{
2570 CheckComArgOutPointerValid(aSessionPID);
2571
2572 AutoCaller autoCaller(this);
2573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2574
2575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 *aSessionPID = mData->mSession.mPID;
2578
2579 return S_OK;
2580}
2581
2582STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2583{
2584 CheckComArgOutPointerValid(machineState);
2585
2586 AutoCaller autoCaller(this);
2587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2588
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 *machineState = mData->mMachineState;
2592
2593 return S_OK;
2594}
2595
2596STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2597{
2598 CheckComArgOutPointerValid(aLastStateChange);
2599
2600 AutoCaller autoCaller(this);
2601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2602
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2606
2607 return S_OK;
2608}
2609
2610STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2611{
2612 CheckComArgOutPointerValid(aStateFilePath);
2613
2614 AutoCaller autoCaller(this);
2615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2616
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2620
2621 return S_OK;
2622}
2623
2624STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2625{
2626 CheckComArgOutPointerValid(aLogFolder);
2627
2628 AutoCaller autoCaller(this);
2629 AssertComRCReturnRC(autoCaller.rc());
2630
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 Utf8Str logFolder;
2634 getLogFolder(logFolder);
2635 logFolder.cloneTo(aLogFolder);
2636
2637 return S_OK;
2638}
2639
2640STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2641{
2642 CheckComArgOutPointerValid(aCurrentSnapshot);
2643
2644 AutoCaller autoCaller(this);
2645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2646
2647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2650
2651 return S_OK;
2652}
2653
2654STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2655{
2656 CheckComArgOutPointerValid(aSnapshotCount);
2657
2658 AutoCaller autoCaller(this);
2659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2660
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2664 ? 0
2665 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2666
2667 return S_OK;
2668}
2669
2670STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2671{
2672 CheckComArgOutPointerValid(aCurrentStateModified);
2673
2674 AutoCaller autoCaller(this);
2675 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2676
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 /* Note: for machines with no snapshots, we always return FALSE
2680 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2681 * reasons :) */
2682
2683 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2684 ? FALSE
2685 : mData->mCurrentStateModified;
2686
2687 return S_OK;
2688}
2689
2690STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2691{
2692 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2693
2694 AutoCaller autoCaller(this);
2695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2696
2697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2698
2699 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2700 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2701
2702 return S_OK;
2703}
2704
2705STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2706{
2707 CheckComArgOutPointerValid(aClipboardMode);
2708
2709 AutoCaller autoCaller(this);
2710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 *aClipboardMode = mHWData->mClipboardMode;
2715
2716 return S_OK;
2717}
2718
2719STDMETHODIMP
2720Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2721{
2722 HRESULT rc = S_OK;
2723
2724 AutoCaller autoCaller(this);
2725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2726
2727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 alock.release();
2730 rc = onClipboardModeChange(aClipboardMode);
2731 alock.acquire();
2732 if (FAILED(rc)) return rc;
2733
2734 setModified(IsModified_MachineData);
2735 mHWData.backup();
2736 mHWData->mClipboardMode = aClipboardMode;
2737
2738 /* Save settings if online - todo why is this required?? */
2739 if (Global::IsOnline(mData->mMachineState))
2740 saveSettings(NULL);
2741
2742 return S_OK;
2743}
2744
2745STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2746{
2747 CheckComArgOutPointerValid(aDragAndDropMode);
2748
2749 AutoCaller autoCaller(this);
2750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2751
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 *aDragAndDropMode = mHWData->mDragAndDropMode;
2755
2756 return S_OK;
2757}
2758
2759STDMETHODIMP
2760Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2761{
2762 HRESULT rc = S_OK;
2763
2764 AutoCaller autoCaller(this);
2765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2766
2767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 alock.release();
2770 rc = onDragAndDropModeChange(aDragAndDropMode);
2771 alock.acquire();
2772 if (FAILED(rc)) return rc;
2773
2774 setModified(IsModified_MachineData);
2775 mHWData.backup();
2776 mHWData->mDragAndDropMode = aDragAndDropMode;
2777
2778 /* Save settings if online - todo why is this required?? */
2779 if (Global::IsOnline(mData->mMachineState))
2780 saveSettings(NULL);
2781
2782 return S_OK;
2783}
2784
2785STDMETHODIMP
2786Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2787{
2788 CheckComArgOutPointerValid(aPatterns);
2789
2790 AutoCaller autoCaller(this);
2791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2792
2793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2794
2795 try
2796 {
2797 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2798 }
2799 catch (...)
2800 {
2801 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2802 }
2803
2804 return S_OK;
2805}
2806
2807STDMETHODIMP
2808Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2809{
2810 AutoCaller autoCaller(this);
2811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2812
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 HRESULT rc = checkStateDependency(MutableStateDep);
2816 if (FAILED(rc)) return rc;
2817
2818 setModified(IsModified_MachineData);
2819 mHWData.backup();
2820 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2821 return rc;
2822}
2823
2824STDMETHODIMP
2825Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2826{
2827 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2835 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2836
2837 return S_OK;
2838}
2839
2840STDMETHODIMP
2841Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2842{
2843 CheckComArgOutPointerValid(aEnabled);
2844
2845 AutoCaller autoCaller(this);
2846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2847
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 *aEnabled = mUserData->s.fTeleporterEnabled;
2851
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2856{
2857 AutoCaller autoCaller(this);
2858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2859
2860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 /* Only allow it to be set to true when PoweredOff or Aborted.
2863 (Clearing it is always permitted.) */
2864 if ( aEnabled
2865 && mData->mRegistered
2866 && ( !isSessionMachine()
2867 || ( mData->mMachineState != MachineState_PoweredOff
2868 && mData->mMachineState != MachineState_Teleported
2869 && mData->mMachineState != MachineState_Aborted
2870 )
2871 )
2872 )
2873 return setError(VBOX_E_INVALID_VM_STATE,
2874 tr("The machine is not powered off (state is %s)"),
2875 Global::stringifyMachineState(mData->mMachineState));
2876
2877 setModified(IsModified_MachineData);
2878 mUserData.backup();
2879 mUserData->s.fTeleporterEnabled = !!aEnabled;
2880
2881 return S_OK;
2882}
2883
2884STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2885{
2886 CheckComArgOutPointerValid(aPort);
2887
2888 AutoCaller autoCaller(this);
2889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2890
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2894
2895 return S_OK;
2896}
2897
2898STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2899{
2900 if (aPort >= _64K)
2901 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2902
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = checkStateDependency(MutableStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 setModified(IsModified_MachineData);
2912 mUserData.backup();
2913 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2919{
2920 CheckComArgOutPointerValid(aAddress);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2928
2929 return S_OK;
2930}
2931
2932STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2933{
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 HRESULT rc = checkStateDependency(MutableStateDep);
2940 if (FAILED(rc)) return rc;
2941
2942 setModified(IsModified_MachineData);
2943 mUserData.backup();
2944 mUserData->s.strTeleporterAddress = aAddress;
2945
2946 return S_OK;
2947}
2948
2949STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2950{
2951 CheckComArgOutPointerValid(aPassword);
2952
2953 AutoCaller autoCaller(this);
2954 HRESULT hrc = autoCaller.rc();
2955 if (SUCCEEDED(hrc))
2956 {
2957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2958 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2959 }
2960
2961 return hrc;
2962}
2963
2964STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2965{
2966 /*
2967 * Hash the password first.
2968 */
2969 Utf8Str strPassword(aPassword);
2970 if (!strPassword.isEmpty())
2971 {
2972 if (VBoxIsPasswordHashed(&strPassword))
2973 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2974 VBoxHashPassword(&strPassword);
2975 }
2976
2977 /*
2978 * Do the update.
2979 */
2980 AutoCaller autoCaller(this);
2981 HRESULT hrc = autoCaller.rc();
2982 if (SUCCEEDED(hrc))
2983 {
2984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2985 hrc = checkStateDependency(MutableStateDep);
2986 if (SUCCEEDED(hrc))
2987 {
2988 setModified(IsModified_MachineData);
2989 mUserData.backup();
2990 mUserData->s.strTeleporterPassword = strPassword;
2991 }
2992 }
2993
2994 return hrc;
2995}
2996
2997STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2998{
2999 CheckComArgOutPointerValid(aState);
3000
3001 AutoCaller autoCaller(this);
3002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3003
3004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3005
3006 *aState = mUserData->s.enmFaultToleranceState;
3007 return S_OK;
3008}
3009
3010STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3011{
3012 AutoCaller autoCaller(this);
3013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3014
3015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 /* @todo deal with running state change. */
3018 HRESULT rc = checkStateDependency(MutableStateDep);
3019 if (FAILED(rc)) return rc;
3020
3021 setModified(IsModified_MachineData);
3022 mUserData.backup();
3023 mUserData->s.enmFaultToleranceState = aState;
3024 return S_OK;
3025}
3026
3027STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3028{
3029 CheckComArgOutPointerValid(aAddress);
3030
3031 AutoCaller autoCaller(this);
3032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3033
3034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3037 return S_OK;
3038}
3039
3040STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3041{
3042 AutoCaller autoCaller(this);
3043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3044
3045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 /* @todo deal with running state change. */
3048 HRESULT rc = checkStateDependency(MutableStateDep);
3049 if (FAILED(rc)) return rc;
3050
3051 setModified(IsModified_MachineData);
3052 mUserData.backup();
3053 mUserData->s.strFaultToleranceAddress = aAddress;
3054 return S_OK;
3055}
3056
3057STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3058{
3059 CheckComArgOutPointerValid(aPort);
3060
3061 AutoCaller autoCaller(this);
3062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3063
3064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 *aPort = mUserData->s.uFaultTolerancePort;
3067 return S_OK;
3068}
3069
3070STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3071{
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 /* @todo deal with running state change. */
3078 HRESULT rc = checkStateDependency(MutableStateDep);
3079 if (FAILED(rc)) return rc;
3080
3081 setModified(IsModified_MachineData);
3082 mUserData.backup();
3083 mUserData->s.uFaultTolerancePort = aPort;
3084 return S_OK;
3085}
3086
3087STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3088{
3089 CheckComArgOutPointerValid(aPassword);
3090
3091 AutoCaller autoCaller(this);
3092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3093
3094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3095
3096 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3097
3098 return S_OK;
3099}
3100
3101STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 /* @todo deal with running state change. */
3109 HRESULT rc = checkStateDependency(MutableStateDep);
3110 if (FAILED(rc)) return rc;
3111
3112 setModified(IsModified_MachineData);
3113 mUserData.backup();
3114 mUserData->s.strFaultTolerancePassword = aPassword;
3115
3116 return S_OK;
3117}
3118
3119STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3120{
3121 CheckComArgOutPointerValid(aInterval);
3122
3123 AutoCaller autoCaller(this);
3124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3125
3126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 *aInterval = mUserData->s.uFaultToleranceInterval;
3129 return S_OK;
3130}
3131
3132STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3133{
3134 AutoCaller autoCaller(this);
3135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /* @todo deal with running state change. */
3140 HRESULT rc = checkStateDependency(MutableStateDep);
3141 if (FAILED(rc)) return rc;
3142
3143 setModified(IsModified_MachineData);
3144 mUserData.backup();
3145 mUserData->s.uFaultToleranceInterval = aInterval;
3146 return S_OK;
3147}
3148
3149STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3150{
3151 CheckComArgOutPointerValid(aEnabled);
3152
3153 AutoCaller autoCaller(this);
3154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3155
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 *aEnabled = mUserData->s.fRTCUseUTC;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3164{
3165 AutoCaller autoCaller(this);
3166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3167
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 /* Only allow it to be set to true when PoweredOff or Aborted.
3171 (Clearing it is always permitted.) */
3172 if ( aEnabled
3173 && mData->mRegistered
3174 && ( !isSessionMachine()
3175 || ( mData->mMachineState != MachineState_PoweredOff
3176 && mData->mMachineState != MachineState_Teleported
3177 && mData->mMachineState != MachineState_Aborted
3178 )
3179 )
3180 )
3181 return setError(VBOX_E_INVALID_VM_STATE,
3182 tr("The machine is not powered off (state is %s)"),
3183 Global::stringifyMachineState(mData->mMachineState));
3184
3185 setModified(IsModified_MachineData);
3186 mUserData.backup();
3187 mUserData->s.fRTCUseUTC = !!aEnabled;
3188
3189 return S_OK;
3190}
3191
3192STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3193{
3194 CheckComArgOutPointerValid(aEnabled);
3195
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 *aEnabled = mHWData->mIOCacheEnabled;
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 HRESULT rc = checkStateDependency(MutableStateDep);
3214 if (FAILED(rc)) return rc;
3215
3216 setModified(IsModified_MachineData);
3217 mHWData.backup();
3218 mHWData->mIOCacheEnabled = aEnabled;
3219
3220 return S_OK;
3221}
3222
3223STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3224{
3225 CheckComArgOutPointerValid(aIOCacheSize);
3226
3227 AutoCaller autoCaller(this);
3228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3229
3230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3231
3232 *aIOCacheSize = mHWData->mIOCacheSize;
3233
3234 return S_OK;
3235}
3236
3237STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3238{
3239 AutoCaller autoCaller(this);
3240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3241
3242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 HRESULT rc = checkStateDependency(MutableStateDep);
3245 if (FAILED(rc)) return rc;
3246
3247 setModified(IsModified_MachineData);
3248 mHWData.backup();
3249 mHWData->mIOCacheSize = aIOCacheSize;
3250
3251 return S_OK;
3252}
3253
3254
3255/**
3256 * @note Locks objects!
3257 */
3258STDMETHODIMP Machine::LockMachine(ISession *aSession,
3259 LockType_T lockType)
3260{
3261 CheckComArgNotNull(aSession);
3262
3263 AutoCaller autoCaller(this);
3264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3265
3266 /* check the session state */
3267 SessionState_T state;
3268 HRESULT rc = aSession->COMGETTER(State)(&state);
3269 if (FAILED(rc)) return rc;
3270
3271 if (state != SessionState_Unlocked)
3272 return setError(VBOX_E_INVALID_OBJECT_STATE,
3273 tr("The given session is busy"));
3274
3275 // get the client's IInternalSessionControl interface
3276 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3277 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3278 E_INVALIDARG);
3279
3280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3281
3282 if (!mData->mRegistered)
3283 return setError(E_UNEXPECTED,
3284 tr("The machine '%s' is not registered"),
3285 mUserData->s.strName.c_str());
3286
3287 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3288
3289 SessionState_T oldState = mData->mSession.mState;
3290 /* Hack: in case the session is closing and there is a progress object
3291 * which allows waiting for the session to be closed, take the opportunity
3292 * and do a limited wait (max. 1 second). This helps a lot when the system
3293 * is busy and thus session closing can take a little while. */
3294 if ( mData->mSession.mState == SessionState_Unlocking
3295 && mData->mSession.mProgress)
3296 {
3297 alock.release();
3298 mData->mSession.mProgress->WaitForCompletion(1000);
3299 alock.acquire();
3300 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3301 }
3302
3303 // try again now
3304 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3305 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3306 )
3307 {
3308 // OK, share the session... we are now dealing with three processes:
3309 // 1) VBoxSVC (where this code runs);
3310 // 2) process C: the caller's client process (who wants a shared session);
3311 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3312
3313 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3314 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3315 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3316 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3317 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3318
3319 /*
3320 * Release the lock before calling the client process. It's safe here
3321 * since the only thing to do after we get the lock again is to add
3322 * the remote control to the list (which doesn't directly influence
3323 * anything).
3324 */
3325 alock.release();
3326
3327 // get the console of the session holding the write lock (this is a remote call)
3328 ComPtr<IConsole> pConsoleW;
3329 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3330 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3331 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3332 if (FAILED(rc))
3333 // the failure may occur w/o any error info (from RPC), so provide one
3334 return setError(VBOX_E_VM_ERROR,
3335 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3336
3337 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3338
3339 // share the session machine and W's console with the caller's session
3340 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3341 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3342 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3343
3344 if (FAILED(rc))
3345 // the failure may occur w/o any error info (from RPC), so provide one
3346 return setError(VBOX_E_VM_ERROR,
3347 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3348 alock.acquire();
3349
3350 // need to revalidate the state after acquiring the lock again
3351 if (mData->mSession.mState != SessionState_Locked)
3352 {
3353 pSessionControl->Uninitialize();
3354 return setError(VBOX_E_INVALID_SESSION_STATE,
3355 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3356 mUserData->s.strName.c_str());
3357 }
3358
3359 // add the caller's session to the list
3360 mData->mSession.mRemoteControls.push_back(pSessionControl);
3361 }
3362 else if ( mData->mSession.mState == SessionState_Locked
3363 || mData->mSession.mState == SessionState_Unlocking
3364 )
3365 {
3366 // sharing not permitted, or machine still unlocking:
3367 return setError(VBOX_E_INVALID_OBJECT_STATE,
3368 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3369 mUserData->s.strName.c_str());
3370 }
3371 else
3372 {
3373 // machine is not locked: then write-lock the machine (create the session machine)
3374
3375 // must not be busy
3376 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3377
3378 // get the caller's session PID
3379 RTPROCESS pid = NIL_RTPROCESS;
3380 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3381 pSessionControl->GetPID((ULONG*)&pid);
3382 Assert(pid != NIL_RTPROCESS);
3383
3384 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3385
3386 if (fLaunchingVMProcess)
3387 {
3388 // this machine is awaiting for a spawning session to be opened:
3389 // then the calling process must be the one that got started by
3390 // LaunchVMProcess()
3391
3392 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3393 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3394
3395 if (mData->mSession.mPID != pid)
3396 return setError(E_ACCESSDENIED,
3397 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3398 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3399 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3400 }
3401
3402 // create the mutable SessionMachine from the current machine
3403 ComObjPtr<SessionMachine> sessionMachine;
3404 sessionMachine.createObject();
3405 rc = sessionMachine->init(this);
3406 AssertComRC(rc);
3407
3408 /* NOTE: doing return from this function after this point but
3409 * before the end is forbidden since it may call SessionMachine::uninit()
3410 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3411 * lock while still holding the Machine lock in alock so that a deadlock
3412 * is possible due to the wrong lock order. */
3413
3414 if (SUCCEEDED(rc))
3415 {
3416 /*
3417 * Set the session state to Spawning to protect against subsequent
3418 * attempts to open a session and to unregister the machine after
3419 * we release the lock.
3420 */
3421 SessionState_T origState = mData->mSession.mState;
3422 mData->mSession.mState = SessionState_Spawning;
3423
3424 /*
3425 * Release the lock before calling the client process -- it will call
3426 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3427 * because the state is Spawning, so that LaunchVMProcess() and
3428 * LockMachine() calls will fail. This method, called before we
3429 * acquire the lock again, will fail because of the wrong PID.
3430 *
3431 * Note that mData->mSession.mRemoteControls accessed outside
3432 * the lock may not be modified when state is Spawning, so it's safe.
3433 */
3434 alock.release();
3435
3436 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3437 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3438 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3439
3440 /* The failure may occur w/o any error info (from RPC), so provide one */
3441 if (FAILED(rc))
3442 setError(VBOX_E_VM_ERROR,
3443 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3444
3445 if ( SUCCEEDED(rc)
3446 && fLaunchingVMProcess
3447 )
3448 {
3449 /* complete the remote session initialization */
3450
3451 /* get the console from the direct session */
3452 ComPtr<IConsole> console;
3453 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3454 ComAssertComRC(rc);
3455
3456 if (SUCCEEDED(rc) && !console)
3457 {
3458 ComAssert(!!console);
3459 rc = E_FAIL;
3460 }
3461
3462 /* assign machine & console to the remote session */
3463 if (SUCCEEDED(rc))
3464 {
3465 /*
3466 * after LaunchVMProcess(), the first and the only
3467 * entry in remoteControls is that remote session
3468 */
3469 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3470 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3471 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3472
3473 /* The failure may occur w/o any error info (from RPC), so provide one */
3474 if (FAILED(rc))
3475 setError(VBOX_E_VM_ERROR,
3476 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3477 }
3478
3479 if (FAILED(rc))
3480 pSessionControl->Uninitialize();
3481 }
3482
3483 /* acquire the lock again */
3484 alock.acquire();
3485
3486 /* Restore the session state */
3487 mData->mSession.mState = origState;
3488 }
3489
3490 // finalize spawning anyway (this is why we don't return on errors above)
3491 if (fLaunchingVMProcess)
3492 {
3493 /* Note that the progress object is finalized later */
3494 /** @todo Consider checking mData->mSession.mProgress for cancellation
3495 * around here. */
3496
3497 /* We don't reset mSession.mPID here because it is necessary for
3498 * SessionMachine::uninit() to reap the child process later. */
3499
3500 if (FAILED(rc))
3501 {
3502 /* Close the remote session, remove the remote control from the list
3503 * and reset session state to Closed (@note keep the code in sync
3504 * with the relevant part in openSession()). */
3505
3506 Assert(mData->mSession.mRemoteControls.size() == 1);
3507 if (mData->mSession.mRemoteControls.size() == 1)
3508 {
3509 ErrorInfoKeeper eik;
3510 mData->mSession.mRemoteControls.front()->Uninitialize();
3511 }
3512
3513 mData->mSession.mRemoteControls.clear();
3514 mData->mSession.mState = SessionState_Unlocked;
3515 }
3516 }
3517 else
3518 {
3519 /* memorize PID of the directly opened session */
3520 if (SUCCEEDED(rc))
3521 mData->mSession.mPID = pid;
3522 }
3523
3524 if (SUCCEEDED(rc))
3525 {
3526 /* memorize the direct session control and cache IUnknown for it */
3527 mData->mSession.mDirectControl = pSessionControl;
3528 mData->mSession.mState = SessionState_Locked;
3529 /* associate the SessionMachine with this Machine */
3530 mData->mSession.mMachine = sessionMachine;
3531
3532 /* request an IUnknown pointer early from the remote party for later
3533 * identity checks (it will be internally cached within mDirectControl
3534 * at least on XPCOM) */
3535 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3536 NOREF(unk);
3537 }
3538
3539 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3540 * would break the lock order */
3541 alock.release();
3542
3543 /* uninitialize the created session machine on failure */
3544 if (FAILED(rc))
3545 sessionMachine->uninit();
3546
3547 }
3548
3549 if (SUCCEEDED(rc))
3550 {
3551 /*
3552 * tell the client watcher thread to update the set of
3553 * machines that have open sessions
3554 */
3555 mParent->updateClientWatcher();
3556
3557 if (oldState != SessionState_Locked)
3558 /* fire an event */
3559 mParent->onSessionStateChange(getId(), SessionState_Locked);
3560 }
3561
3562 return rc;
3563}
3564
3565/**
3566 * @note Locks objects!
3567 */
3568STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3569 IN_BSTR aFrontend,
3570 IN_BSTR aEnvironment,
3571 IProgress **aProgress)
3572{
3573 CheckComArgStr(aFrontend);
3574 Utf8Str strFrontend(aFrontend);
3575 Utf8Str strEnvironment(aEnvironment);
3576 /* "emergencystop" doesn't need the session, so skip the checks/interface
3577 * retrieval. This code doesn't quite fit in here, but introducing a
3578 * special API method would be even more effort, and would require explicit
3579 * support by every API client. It's better to hide the feature a bit. */
3580 if (strFrontend != "emergencystop")
3581 CheckComArgNotNull(aSession);
3582 CheckComArgOutPointerValid(aProgress);
3583
3584 AutoCaller autoCaller(this);
3585 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3586
3587 HRESULT rc = S_OK;
3588 if (strFrontend.isEmpty())
3589 {
3590 Bstr bstrFrontend;
3591 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3592 if (FAILED(rc))
3593 return rc;
3594 strFrontend = bstrFrontend;
3595 if (strFrontend.isEmpty())
3596 {
3597 ComPtr<ISystemProperties> systemProperties;
3598 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3599 if (FAILED(rc))
3600 return rc;
3601 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3602 if (FAILED(rc))
3603 return rc;
3604 strFrontend = bstrFrontend;
3605 }
3606 /* paranoia - emergencystop is not a valid default */
3607 if (strFrontend == "emergencystop")
3608 strFrontend = Utf8Str::Empty;
3609 }
3610
3611 if (strFrontend != "emergencystop")
3612 {
3613 /* check the session state */
3614 SessionState_T state;
3615 rc = aSession->COMGETTER(State)(&state);
3616 if (FAILED(rc))
3617 return rc;
3618
3619 if (state != SessionState_Unlocked)
3620 return setError(VBOX_E_INVALID_OBJECT_STATE,
3621 tr("The given session is busy"));
3622
3623 /* get the IInternalSessionControl interface */
3624 ComPtr<IInternalSessionControl> control(aSession);
3625 ComAssertMsgRet(!control.isNull(),
3626 ("No IInternalSessionControl interface"),
3627 E_INVALIDARG);
3628
3629 /* get the teleporter enable state for the progress object init. */
3630 BOOL fTeleporterEnabled;
3631 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3632 if (FAILED(rc))
3633 return rc;
3634
3635 /* create a progress object */
3636 ComObjPtr<ProgressProxy> progress;
3637 progress.createObject();
3638 rc = progress->init(mParent,
3639 static_cast<IMachine*>(this),
3640 Bstr(tr("Starting VM")).raw(),
3641 TRUE /* aCancelable */,
3642 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3643 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3644 2 /* uFirstOperationWeight */,
3645 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3646
3647 if (SUCCEEDED(rc))
3648 {
3649 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3650 if (SUCCEEDED(rc))
3651 {
3652 progress.queryInterfaceTo(aProgress);
3653
3654 /* signal the client watcher thread */
3655 mParent->updateClientWatcher();
3656
3657 /* fire an event */
3658 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3659 }
3660 }
3661 }
3662 else
3663 {
3664 /* no progress object - either instant success or failure */
3665 *aProgress = NULL;
3666
3667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3668
3669 if (mData->mSession.mState != SessionState_Locked)
3670 return setError(VBOX_E_INVALID_OBJECT_STATE,
3671 tr("The machine '%s' is not locked by a session"),
3672 mUserData->s.strName.c_str());
3673
3674 /* must have a VM process associated - do not kill normal API clients
3675 * with an open session */
3676 if (!Global::IsOnline(mData->mMachineState))
3677 return setError(VBOX_E_INVALID_OBJECT_STATE,
3678 tr("The machine '%s' does not have a VM process"),
3679 mUserData->s.strName.c_str());
3680
3681 /* forcibly terminate the VM process */
3682 if (mData->mSession.mPID != NIL_RTPROCESS)
3683 RTProcTerminate(mData->mSession.mPID);
3684
3685 /* signal the client watcher thread, as most likely the client has
3686 * been terminated */
3687 mParent->updateClientWatcher();
3688 }
3689
3690 return rc;
3691}
3692
3693STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3694{
3695 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3696 return setError(E_INVALIDARG,
3697 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3698 aPosition, SchemaDefs::MaxBootPosition);
3699
3700 if (aDevice == DeviceType_USB)
3701 return setError(E_NOTIMPL,
3702 tr("Booting from USB device is currently not supported"));
3703
3704 AutoCaller autoCaller(this);
3705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3706
3707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3708
3709 HRESULT rc = checkStateDependency(MutableStateDep);
3710 if (FAILED(rc)) return rc;
3711
3712 setModified(IsModified_MachineData);
3713 mHWData.backup();
3714 mHWData->mBootOrder[aPosition - 1] = aDevice;
3715
3716 return S_OK;
3717}
3718
3719STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3720{
3721 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3722 return setError(E_INVALIDARG,
3723 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3724 aPosition, SchemaDefs::MaxBootPosition);
3725
3726 AutoCaller autoCaller(this);
3727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3728
3729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3730
3731 *aDevice = mHWData->mBootOrder[aPosition - 1];
3732
3733 return S_OK;
3734}
3735
3736STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3737 LONG aControllerPort,
3738 LONG aDevice,
3739 DeviceType_T aType,
3740 IMedium *aMedium)
3741{
3742 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3743 aControllerName, aControllerPort, aDevice, aType, aMedium));
3744
3745 CheckComArgStrNotEmptyOrNull(aControllerName);
3746
3747 AutoCaller autoCaller(this);
3748 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3749
3750 // request the host lock first, since might be calling Host methods for getting host drives;
3751 // next, protect the media tree all the while we're in here, as well as our member variables
3752 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3753 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3754
3755 HRESULT rc = checkStateDependency(MutableStateDep);
3756 if (FAILED(rc)) return rc;
3757
3758 /// @todo NEWMEDIA implicit machine registration
3759 if (!mData->mRegistered)
3760 return setError(VBOX_E_INVALID_OBJECT_STATE,
3761 tr("Cannot attach storage devices to an unregistered machine"));
3762
3763 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3764
3765 /* Check for an existing controller. */
3766 ComObjPtr<StorageController> ctl;
3767 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3768 if (FAILED(rc)) return rc;
3769
3770 StorageControllerType_T ctrlType;
3771 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3772 if (FAILED(rc))
3773 return setError(E_FAIL,
3774 tr("Could not get type of controller '%ls'"),
3775 aControllerName);
3776
3777 bool fSilent = false;
3778 Utf8Str strReconfig;
3779
3780 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3781 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3782 if (FAILED(rc))
3783 return rc;
3784 if ( mData->mMachineState == MachineState_Paused
3785 && strReconfig == "1")
3786 fSilent = true;
3787
3788 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3789 bool fHotplug = false;
3790 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3791 fHotplug = true;
3792
3793 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3794 return setError(VBOX_E_INVALID_VM_STATE,
3795 tr("Controller '%ls' does not support hotplugging"),
3796 aControllerName);
3797
3798 // check that the port and device are not out of range
3799 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3800 if (FAILED(rc)) return rc;
3801
3802 /* check if the device slot is already busy */
3803 MediumAttachment *pAttachTemp;
3804 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3805 aControllerName,
3806 aControllerPort,
3807 aDevice)))
3808 {
3809 Medium *pMedium = pAttachTemp->getMedium();
3810 if (pMedium)
3811 {
3812 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3813 return setError(VBOX_E_OBJECT_IN_USE,
3814 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3815 pMedium->getLocationFull().c_str(),
3816 aControllerPort,
3817 aDevice,
3818 aControllerName);
3819 }
3820 else
3821 return setError(VBOX_E_OBJECT_IN_USE,
3822 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3823 aControllerPort, aDevice, aControllerName);
3824 }
3825
3826 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3827 if (aMedium && medium.isNull())
3828 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3829
3830 AutoCaller mediumCaller(medium);
3831 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3832
3833 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3834
3835 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3836 && !medium.isNull()
3837 )
3838 return setError(VBOX_E_OBJECT_IN_USE,
3839 tr("Medium '%s' is already attached to this virtual machine"),
3840 medium->getLocationFull().c_str());
3841
3842 if (!medium.isNull())
3843 {
3844 MediumType_T mtype = medium->getType();
3845 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3846 // For DVDs it's not written to the config file, so needs no global config
3847 // version bump. For floppies it's a new attribute "type", which is ignored
3848 // by older VirtualBox version, so needs no global config version bump either.
3849 // For hard disks this type is not accepted.
3850 if (mtype == MediumType_MultiAttach)
3851 {
3852 // This type is new with VirtualBox 4.0 and therefore requires settings
3853 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3854 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3855 // two reasons: The medium type is a property of the media registry tree, which
3856 // can reside in the global config file (for pre-4.0 media); we would therefore
3857 // possibly need to bump the global config version. We don't want to do that though
3858 // because that might make downgrading to pre-4.0 impossible.
3859 // As a result, we can only use these two new types if the medium is NOT in the
3860 // global registry:
3861 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3862 if ( medium->isInRegistry(uuidGlobalRegistry)
3863 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3864 )
3865 return setError(VBOX_E_INVALID_OBJECT_STATE,
3866 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3867 "to machines that were created with VirtualBox 4.0 or later"),
3868 medium->getLocationFull().c_str());
3869 }
3870 }
3871
3872 bool fIndirect = false;
3873 if (!medium.isNull())
3874 fIndirect = medium->isReadOnly();
3875 bool associate = true;
3876
3877 do
3878 {
3879 if ( aType == DeviceType_HardDisk
3880 && mMediaData.isBackedUp())
3881 {
3882 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3883
3884 /* check if the medium was attached to the VM before we started
3885 * changing attachments in which case the attachment just needs to
3886 * be restored */
3887 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3888 {
3889 AssertReturn(!fIndirect, E_FAIL);
3890
3891 /* see if it's the same bus/channel/device */
3892 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3893 {
3894 /* the simplest case: restore the whole attachment
3895 * and return, nothing else to do */
3896 mMediaData->mAttachments.push_back(pAttachTemp);
3897 return S_OK;
3898 }
3899
3900 /* bus/channel/device differ; we need a new attachment object,
3901 * but don't try to associate it again */
3902 associate = false;
3903 break;
3904 }
3905 }
3906
3907 /* go further only if the attachment is to be indirect */
3908 if (!fIndirect)
3909 break;
3910
3911 /* perform the so called smart attachment logic for indirect
3912 * attachments. Note that smart attachment is only applicable to base
3913 * hard disks. */
3914
3915 if (medium->getParent().isNull())
3916 {
3917 /* first, investigate the backup copy of the current hard disk
3918 * attachments to make it possible to re-attach existing diffs to
3919 * another device slot w/o losing their contents */
3920 if (mMediaData.isBackedUp())
3921 {
3922 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3923
3924 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3925 uint32_t foundLevel = 0;
3926
3927 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3928 it != oldAtts.end();
3929 ++it)
3930 {
3931 uint32_t level = 0;
3932 MediumAttachment *pAttach = *it;
3933 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3934 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3935 if (pMedium.isNull())
3936 continue;
3937
3938 if (pMedium->getBase(&level) == medium)
3939 {
3940 /* skip the hard disk if its currently attached (we
3941 * cannot attach the same hard disk twice) */
3942 if (findAttachment(mMediaData->mAttachments,
3943 pMedium))
3944 continue;
3945
3946 /* matched device, channel and bus (i.e. attached to the
3947 * same place) will win and immediately stop the search;
3948 * otherwise the attachment that has the youngest
3949 * descendant of medium will be used
3950 */
3951 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3952 {
3953 /* the simplest case: restore the whole attachment
3954 * and return, nothing else to do */
3955 mMediaData->mAttachments.push_back(*it);
3956 return S_OK;
3957 }
3958 else if ( foundIt == oldAtts.end()
3959 || level > foundLevel /* prefer younger */
3960 )
3961 {
3962 foundIt = it;
3963 foundLevel = level;
3964 }
3965 }
3966 }
3967
3968 if (foundIt != oldAtts.end())
3969 {
3970 /* use the previously attached hard disk */
3971 medium = (*foundIt)->getMedium();
3972 mediumCaller.attach(medium);
3973 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3974 mediumLock.attach(medium);
3975 /* not implicit, doesn't require association with this VM */
3976 fIndirect = false;
3977 associate = false;
3978 /* go right to the MediumAttachment creation */
3979 break;
3980 }
3981 }
3982
3983 /* must give up the medium lock and medium tree lock as below we
3984 * go over snapshots, which needs a lock with higher lock order. */
3985 mediumLock.release();
3986 treeLock.release();
3987
3988 /* then, search through snapshots for the best diff in the given
3989 * hard disk's chain to base the new diff on */
3990
3991 ComObjPtr<Medium> base;
3992 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3993 while (snap)
3994 {
3995 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3996
3997 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3998
3999 MediumAttachment *pAttachFound = NULL;
4000 uint32_t foundLevel = 0;
4001
4002 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4003 it != snapAtts.end();
4004 ++it)
4005 {
4006 MediumAttachment *pAttach = *it;
4007 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4008 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4009 if (pMedium.isNull())
4010 continue;
4011
4012 uint32_t level = 0;
4013 if (pMedium->getBase(&level) == medium)
4014 {
4015 /* matched device, channel and bus (i.e. attached to the
4016 * same place) will win and immediately stop the search;
4017 * otherwise the attachment that has the youngest
4018 * descendant of medium will be used
4019 */
4020 if ( pAttach->getDevice() == aDevice
4021 && pAttach->getPort() == aControllerPort
4022 && pAttach->getControllerName() == aControllerName
4023 )
4024 {
4025 pAttachFound = pAttach;
4026 break;
4027 }
4028 else if ( !pAttachFound
4029 || level > foundLevel /* prefer younger */
4030 )
4031 {
4032 pAttachFound = pAttach;
4033 foundLevel = level;
4034 }
4035 }
4036 }
4037
4038 if (pAttachFound)
4039 {
4040 base = pAttachFound->getMedium();
4041 break;
4042 }
4043
4044 snap = snap->getParent();
4045 }
4046
4047 /* re-lock medium tree and the medium, as we need it below */
4048 treeLock.acquire();
4049 mediumLock.acquire();
4050
4051 /* found a suitable diff, use it as a base */
4052 if (!base.isNull())
4053 {
4054 medium = base;
4055 mediumCaller.attach(medium);
4056 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4057 mediumLock.attach(medium);
4058 }
4059 }
4060
4061 Utf8Str strFullSnapshotFolder;
4062 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4063
4064 ComObjPtr<Medium> diff;
4065 diff.createObject();
4066 // store this diff in the same registry as the parent
4067 Guid uuidRegistryParent;
4068 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4069 {
4070 // parent image has no registry: this can happen if we're attaching a new immutable
4071 // image that has not yet been attached (medium then points to the base and we're
4072 // creating the diff image for the immutable, and the parent is not yet registered);
4073 // put the parent in the machine registry then
4074 mediumLock.release();
4075 treeLock.release();
4076 alock.release();
4077 addMediumToRegistry(medium);
4078 alock.acquire();
4079 treeLock.acquire();
4080 mediumLock.acquire();
4081 medium->getFirstRegistryMachineId(uuidRegistryParent);
4082 }
4083 rc = diff->init(mParent,
4084 medium->getPreferredDiffFormat(),
4085 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4086 uuidRegistryParent);
4087 if (FAILED(rc)) return rc;
4088
4089 /* Apply the normal locking logic to the entire chain. */
4090 MediumLockList *pMediumLockList(new MediumLockList());
4091 mediumLock.release();
4092 treeLock.release();
4093 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4094 true /* fMediumLockWrite */,
4095 medium,
4096 *pMediumLockList);
4097 treeLock.acquire();
4098 mediumLock.acquire();
4099 if (SUCCEEDED(rc))
4100 {
4101 mediumLock.release();
4102 treeLock.release();
4103 rc = pMediumLockList->Lock();
4104 treeLock.acquire();
4105 mediumLock.acquire();
4106 if (FAILED(rc))
4107 setError(rc,
4108 tr("Could not lock medium when creating diff '%s'"),
4109 diff->getLocationFull().c_str());
4110 else
4111 {
4112 /* will release the lock before the potentially lengthy
4113 * operation, so protect with the special state */
4114 MachineState_T oldState = mData->mMachineState;
4115 setMachineState(MachineState_SettingUp);
4116
4117 mediumLock.release();
4118 treeLock.release();
4119 alock.release();
4120
4121 rc = medium->createDiffStorage(diff,
4122 MediumVariant_Standard,
4123 pMediumLockList,
4124 NULL /* aProgress */,
4125 true /* aWait */);
4126
4127 alock.acquire();
4128 treeLock.acquire();
4129 mediumLock.acquire();
4130
4131 setMachineState(oldState);
4132 }
4133 }
4134
4135 /* Unlock the media and free the associated memory. */
4136 delete pMediumLockList;
4137
4138 if (FAILED(rc)) return rc;
4139
4140 /* use the created diff for the actual attachment */
4141 medium = diff;
4142 mediumCaller.attach(medium);
4143 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4144 mediumLock.attach(medium);
4145 }
4146 while (0);
4147
4148 ComObjPtr<MediumAttachment> attachment;
4149 attachment.createObject();
4150 rc = attachment->init(this,
4151 medium,
4152 aControllerName,
4153 aControllerPort,
4154 aDevice,
4155 aType,
4156 fIndirect,
4157 false /* fPassthrough */,
4158 false /* fTempEject */,
4159 false /* fNonRotational */,
4160 false /* fDiscard */,
4161 Utf8Str::Empty);
4162 if (FAILED(rc)) return rc;
4163
4164 if (associate && !medium.isNull())
4165 {
4166 // as the last step, associate the medium to the VM
4167 rc = medium->addBackReference(mData->mUuid);
4168 // here we can fail because of Deleting, or being in process of creating a Diff
4169 if (FAILED(rc)) return rc;
4170
4171 mediumLock.release();
4172 treeLock.release();
4173 alock.release();
4174 addMediumToRegistry(medium);
4175 alock.acquire();
4176 treeLock.acquire();
4177 mediumLock.acquire();
4178 }
4179
4180 /* success: finally remember the attachment */
4181 setModified(IsModified_Storage);
4182 mMediaData.backup();
4183 mMediaData->mAttachments.push_back(attachment);
4184
4185 mediumLock.release();
4186 treeLock.release();
4187 alock.release();
4188
4189 if (fHotplug || fSilent)
4190 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4191
4192 mParent->saveModifiedRegistries();
4193
4194 return rc;
4195}
4196
4197STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4198 LONG aDevice)
4199{
4200 CheckComArgStrNotEmptyOrNull(aControllerName);
4201
4202 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4203 aControllerName, aControllerPort, aDevice));
4204
4205 AutoCaller autoCaller(this);
4206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4207
4208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4209
4210 HRESULT rc = checkStateDependency(MutableStateDep);
4211 if (FAILED(rc)) return rc;
4212
4213 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4214
4215 /* Check for an existing controller. */
4216 ComObjPtr<StorageController> ctl;
4217 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4218 if (FAILED(rc)) return rc;
4219
4220 StorageControllerType_T ctrlType;
4221 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4222 if (FAILED(rc))
4223 return setError(E_FAIL,
4224 tr("Could not get type of controller '%ls'"),
4225 aControllerName);
4226
4227 bool fSilent = false;
4228 Utf8Str strReconfig;
4229
4230 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4231 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4232 if (FAILED(rc))
4233 return rc;
4234 if ( mData->mMachineState == MachineState_Paused
4235 && strReconfig == "1")
4236 fSilent = true;
4237
4238 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4239 bool fHotplug = false;
4240 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4241 fHotplug = true;
4242
4243 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4244 return setError(VBOX_E_INVALID_VM_STATE,
4245 tr("Controller '%ls' does not support hotplugging"),
4246 aControllerName);
4247
4248 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4249 aControllerName,
4250 aControllerPort,
4251 aDevice);
4252 if (!pAttach)
4253 return setError(VBOX_E_OBJECT_NOT_FOUND,
4254 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4255 aDevice, aControllerPort, aControllerName);
4256
4257 /*
4258 * The VM has to detach the device before we delete any implicit diffs.
4259 * If this fails we can roll back without loosing data.
4260 */
4261 if (fHotplug || fSilent)
4262 {
4263 alock.release();
4264 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4265 alock.acquire();
4266 }
4267 if (FAILED(rc)) return rc;
4268
4269 /* If we are here everything went well and we can delete the implicit now. */
4270 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4271
4272 alock.release();
4273
4274 mParent->saveModifiedRegistries();
4275
4276 return rc;
4277}
4278
4279STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4280 LONG aDevice, BOOL aPassthrough)
4281{
4282 CheckComArgStrNotEmptyOrNull(aControllerName);
4283
4284 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4285 aControllerName, aControllerPort, aDevice, aPassthrough));
4286
4287 AutoCaller autoCaller(this);
4288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4289
4290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4291
4292 HRESULT rc = checkStateDependency(MutableStateDep);
4293 if (FAILED(rc)) return rc;
4294
4295 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4296
4297 if (Global::IsOnlineOrTransient(mData->mMachineState))
4298 return setError(VBOX_E_INVALID_VM_STATE,
4299 tr("Invalid machine state: %s"),
4300 Global::stringifyMachineState(mData->mMachineState));
4301
4302 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4303 aControllerName,
4304 aControllerPort,
4305 aDevice);
4306 if (!pAttach)
4307 return setError(VBOX_E_OBJECT_NOT_FOUND,
4308 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4309 aDevice, aControllerPort, aControllerName);
4310
4311
4312 setModified(IsModified_Storage);
4313 mMediaData.backup();
4314
4315 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4316
4317 if (pAttach->getType() != DeviceType_DVD)
4318 return setError(E_INVALIDARG,
4319 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4320 aDevice, aControllerPort, aControllerName);
4321 pAttach->updatePassthrough(!!aPassthrough);
4322
4323 return S_OK;
4324}
4325
4326STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4327 LONG aDevice, BOOL aTemporaryEject)
4328{
4329 CheckComArgStrNotEmptyOrNull(aControllerName);
4330
4331 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4332 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4333
4334 AutoCaller autoCaller(this);
4335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4336
4337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4338
4339 HRESULT rc = checkStateDependency(MutableStateDep);
4340 if (FAILED(rc)) return rc;
4341
4342 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4343 aControllerName,
4344 aControllerPort,
4345 aDevice);
4346 if (!pAttach)
4347 return setError(VBOX_E_OBJECT_NOT_FOUND,
4348 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4349 aDevice, aControllerPort, aControllerName);
4350
4351
4352 setModified(IsModified_Storage);
4353 mMediaData.backup();
4354
4355 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4356
4357 if (pAttach->getType() != DeviceType_DVD)
4358 return setError(E_INVALIDARG,
4359 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4360 aDevice, aControllerPort, aControllerName);
4361 pAttach->updateTempEject(!!aTemporaryEject);
4362
4363 return S_OK;
4364}
4365
4366STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4367 LONG aDevice, BOOL aNonRotational)
4368{
4369 CheckComArgStrNotEmptyOrNull(aControllerName);
4370
4371 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4372 aControllerName, aControllerPort, aDevice, aNonRotational));
4373
4374 AutoCaller autoCaller(this);
4375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4376
4377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4378
4379 HRESULT rc = checkStateDependency(MutableStateDep);
4380 if (FAILED(rc)) return rc;
4381
4382 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4383
4384 if (Global::IsOnlineOrTransient(mData->mMachineState))
4385 return setError(VBOX_E_INVALID_VM_STATE,
4386 tr("Invalid machine state: %s"),
4387 Global::stringifyMachineState(mData->mMachineState));
4388
4389 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4390 aControllerName,
4391 aControllerPort,
4392 aDevice);
4393 if (!pAttach)
4394 return setError(VBOX_E_OBJECT_NOT_FOUND,
4395 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4396 aDevice, aControllerPort, aControllerName);
4397
4398
4399 setModified(IsModified_Storage);
4400 mMediaData.backup();
4401
4402 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4403
4404 if (pAttach->getType() != DeviceType_HardDisk)
4405 return setError(E_INVALIDARG,
4406 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"),
4407 aDevice, aControllerPort, aControllerName);
4408 pAttach->updateNonRotational(!!aNonRotational);
4409
4410 return S_OK;
4411}
4412
4413STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4414 LONG aDevice, BOOL aDiscard)
4415{
4416 CheckComArgStrNotEmptyOrNull(aControllerName);
4417
4418 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4419 aControllerName, aControllerPort, aDevice, aDiscard));
4420
4421 AutoCaller autoCaller(this);
4422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4423
4424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4425
4426 HRESULT rc = checkStateDependency(MutableStateDep);
4427 if (FAILED(rc)) return rc;
4428
4429 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4430
4431 if (Global::IsOnlineOrTransient(mData->mMachineState))
4432 return setError(VBOX_E_INVALID_VM_STATE,
4433 tr("Invalid machine state: %s"),
4434 Global::stringifyMachineState(mData->mMachineState));
4435
4436 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4437 aControllerName,
4438 aControllerPort,
4439 aDevice);
4440 if (!pAttach)
4441 return setError(VBOX_E_OBJECT_NOT_FOUND,
4442 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4443 aDevice, aControllerPort, aControllerName);
4444
4445
4446 setModified(IsModified_Storage);
4447 mMediaData.backup();
4448
4449 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4450
4451 if (pAttach->getType() != DeviceType_HardDisk)
4452 return setError(E_INVALIDARG,
4453 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"),
4454 aDevice, aControllerPort, aControllerName);
4455 pAttach->updateDiscard(!!aDiscard);
4456
4457 return S_OK;
4458}
4459
4460STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4461 LONG aDevice)
4462{
4463 int rc = S_OK;
4464 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4465 aControllerName, aControllerPort, aDevice));
4466
4467 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4468
4469 return rc;
4470}
4471
4472STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4473 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4474{
4475 CheckComArgStrNotEmptyOrNull(aControllerName);
4476
4477 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4478 aControllerName, aControllerPort, aDevice));
4479
4480 AutoCaller autoCaller(this);
4481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4482
4483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4484
4485 HRESULT rc = checkStateDependency(MutableStateDep);
4486 if (FAILED(rc)) return rc;
4487
4488 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4489
4490 if (Global::IsOnlineOrTransient(mData->mMachineState))
4491 return setError(VBOX_E_INVALID_VM_STATE,
4492 tr("Invalid machine state: %s"),
4493 Global::stringifyMachineState(mData->mMachineState));
4494
4495 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4496 aControllerName,
4497 aControllerPort,
4498 aDevice);
4499 if (!pAttach)
4500 return setError(VBOX_E_OBJECT_NOT_FOUND,
4501 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4502 aDevice, aControllerPort, aControllerName);
4503
4504
4505 setModified(IsModified_Storage);
4506 mMediaData.backup();
4507
4508 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4509 if (aBandwidthGroup && group.isNull())
4510 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4511
4512 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4513
4514 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4515 if (strBandwidthGroupOld.isNotEmpty())
4516 {
4517 /* Get the bandwidth group object and release it - this must not fail. */
4518 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4519 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4520 Assert(SUCCEEDED(rc));
4521
4522 pBandwidthGroupOld->release();
4523 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4524 }
4525
4526 if (!group.isNull())
4527 {
4528 group->reference();
4529 pAttach->updateBandwidthGroup(group->getName());
4530 }
4531
4532 return S_OK;
4533}
4534
4535STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4536 LONG aControllerPort,
4537 LONG aDevice,
4538 DeviceType_T aType)
4539{
4540 HRESULT rc = S_OK;
4541
4542 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4543 aControllerName, aControllerPort, aDevice, aType));
4544
4545 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4546
4547 return rc;
4548}
4549
4550
4551
4552STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4553 LONG aControllerPort,
4554 LONG aDevice,
4555 BOOL aForce)
4556{
4557 int rc = S_OK;
4558 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4559 aControllerName, aControllerPort, aForce));
4560
4561 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4562
4563 return rc;
4564}
4565
4566STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4567 LONG aControllerPort,
4568 LONG aDevice,
4569 IMedium *aMedium,
4570 BOOL aForce)
4571{
4572 int rc = S_OK;
4573 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4574 aControllerName, aControllerPort, aDevice, aForce));
4575
4576 CheckComArgStrNotEmptyOrNull(aControllerName);
4577
4578 AutoCaller autoCaller(this);
4579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4580
4581 // request the host lock first, since might be calling Host methods for getting host drives;
4582 // next, protect the media tree all the while we're in here, as well as our member variables
4583 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4584 this->lockHandle(),
4585 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4586
4587 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4588 aControllerName,
4589 aControllerPort,
4590 aDevice);
4591 if (pAttach.isNull())
4592 return setError(VBOX_E_OBJECT_NOT_FOUND,
4593 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4594 aDevice, aControllerPort, aControllerName);
4595
4596 /* Remember previously mounted medium. The medium before taking the
4597 * backup is not necessarily the same thing. */
4598 ComObjPtr<Medium> oldmedium;
4599 oldmedium = pAttach->getMedium();
4600
4601 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4602 if (aMedium && pMedium.isNull())
4603 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4604
4605 AutoCaller mediumCaller(pMedium);
4606 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4607
4608 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4609 if (pMedium)
4610 {
4611 DeviceType_T mediumType = pAttach->getType();
4612 switch (mediumType)
4613 {
4614 case DeviceType_DVD:
4615 case DeviceType_Floppy:
4616 break;
4617
4618 default:
4619 return setError(VBOX_E_INVALID_OBJECT_STATE,
4620 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4621 aControllerPort,
4622 aDevice,
4623 aControllerName);
4624 }
4625 }
4626
4627 setModified(IsModified_Storage);
4628 mMediaData.backup();
4629
4630 {
4631 // The backup operation makes the pAttach reference point to the
4632 // old settings. Re-get the correct reference.
4633 pAttach = findAttachment(mMediaData->mAttachments,
4634 aControllerName,
4635 aControllerPort,
4636 aDevice);
4637 if (!oldmedium.isNull())
4638 oldmedium->removeBackReference(mData->mUuid);
4639 if (!pMedium.isNull())
4640 {
4641 pMedium->addBackReference(mData->mUuid);
4642
4643 mediumLock.release();
4644 multiLock.release();
4645 addMediumToRegistry(pMedium);
4646 multiLock.acquire();
4647 mediumLock.acquire();
4648 }
4649
4650 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4651 pAttach->updateMedium(pMedium);
4652 }
4653
4654 setModified(IsModified_Storage);
4655
4656 mediumLock.release();
4657 multiLock.release();
4658 rc = onMediumChange(pAttach, aForce);
4659 multiLock.acquire();
4660 mediumLock.acquire();
4661
4662 /* On error roll back this change only. */
4663 if (FAILED(rc))
4664 {
4665 if (!pMedium.isNull())
4666 pMedium->removeBackReference(mData->mUuid);
4667 pAttach = findAttachment(mMediaData->mAttachments,
4668 aControllerName,
4669 aControllerPort,
4670 aDevice);
4671 /* If the attachment is gone in the meantime, bail out. */
4672 if (pAttach.isNull())
4673 return rc;
4674 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4675 if (!oldmedium.isNull())
4676 oldmedium->addBackReference(mData->mUuid);
4677 pAttach->updateMedium(oldmedium);
4678 }
4679
4680 mediumLock.release();
4681 multiLock.release();
4682
4683 mParent->saveModifiedRegistries();
4684
4685 return rc;
4686}
4687
4688STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4689 LONG aControllerPort,
4690 LONG aDevice,
4691 IMedium **aMedium)
4692{
4693 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4694 aControllerName, aControllerPort, aDevice));
4695
4696 CheckComArgStrNotEmptyOrNull(aControllerName);
4697 CheckComArgOutPointerValid(aMedium);
4698
4699 AutoCaller autoCaller(this);
4700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4701
4702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4703
4704 *aMedium = NULL;
4705
4706 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4707 aControllerName,
4708 aControllerPort,
4709 aDevice);
4710 if (pAttach.isNull())
4711 return setError(VBOX_E_OBJECT_NOT_FOUND,
4712 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4713 aDevice, aControllerPort, aControllerName);
4714
4715 pAttach->getMedium().queryInterfaceTo(aMedium);
4716
4717 return S_OK;
4718}
4719
4720STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4721{
4722 CheckComArgOutPointerValid(port);
4723 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4724
4725 AutoCaller autoCaller(this);
4726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4727
4728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4729
4730 mSerialPorts[slot].queryInterfaceTo(port);
4731
4732 return S_OK;
4733}
4734
4735STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4736{
4737 CheckComArgOutPointerValid(port);
4738 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4739
4740 AutoCaller autoCaller(this);
4741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4742
4743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4744
4745 mParallelPorts[slot].queryInterfaceTo(port);
4746
4747 return S_OK;
4748}
4749
4750STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4751{
4752 CheckComArgOutPointerValid(adapter);
4753 /* Do not assert if slot is out of range, just return the advertised
4754 status. testdriver/vbox.py triggers this in logVmInfo. */
4755 if (slot >= mNetworkAdapters.size())
4756 return setError(E_INVALIDARG,
4757 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4758 slot, mNetworkAdapters.size());
4759
4760 AutoCaller autoCaller(this);
4761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4762
4763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4764
4765 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4766
4767 return S_OK;
4768}
4769
4770STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4771{
4772 CheckComArgOutSafeArrayPointerValid(aKeys);
4773
4774 AutoCaller autoCaller(this);
4775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4776
4777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4778
4779 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4780 int i = 0;
4781 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4782 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4783 ++it, ++i)
4784 {
4785 const Utf8Str &strKey = it->first;
4786 strKey.cloneTo(&saKeys[i]);
4787 }
4788 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4789
4790 return S_OK;
4791 }
4792
4793 /**
4794 * @note Locks this object for reading.
4795 */
4796STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4797 BSTR *aValue)
4798{
4799 CheckComArgStrNotEmptyOrNull(aKey);
4800 CheckComArgOutPointerValid(aValue);
4801
4802 AutoCaller autoCaller(this);
4803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4804
4805 /* start with nothing found */
4806 Bstr bstrResult("");
4807
4808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4809
4810 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4811 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4812 // found:
4813 bstrResult = it->second; // source is a Utf8Str
4814
4815 /* return the result to caller (may be empty) */
4816 bstrResult.cloneTo(aValue);
4817
4818 return S_OK;
4819}
4820
4821 /**
4822 * @note Locks mParent for writing + this object for writing.
4823 */
4824STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4825{
4826 CheckComArgStrNotEmptyOrNull(aKey);
4827
4828 AutoCaller autoCaller(this);
4829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4830
4831 Utf8Str strKey(aKey);
4832 Utf8Str strValue(aValue);
4833 Utf8Str strOldValue; // empty
4834
4835 // locking note: we only hold the read lock briefly to look up the old value,
4836 // then release it and call the onExtraCanChange callbacks. There is a small
4837 // chance of a race insofar as the callback might be called twice if two callers
4838 // change the same key at the same time, but that's a much better solution
4839 // than the deadlock we had here before. The actual changing of the extradata
4840 // is then performed under the write lock and race-free.
4841
4842 // look up the old value first; if nothing has changed then we need not do anything
4843 {
4844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4845 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4846 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4847 strOldValue = it->second;
4848 }
4849
4850 bool fChanged;
4851 if ((fChanged = (strOldValue != strValue)))
4852 {
4853 // ask for permission from all listeners outside the locks;
4854 // onExtraDataCanChange() only briefly requests the VirtualBox
4855 // lock to copy the list of callbacks to invoke
4856 Bstr error;
4857 Bstr bstrValue(aValue);
4858
4859 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4860 {
4861 const char *sep = error.isEmpty() ? "" : ": ";
4862 CBSTR err = error.raw();
4863 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4864 sep, err));
4865 return setError(E_ACCESSDENIED,
4866 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4867 aKey,
4868 bstrValue.raw(),
4869 sep,
4870 err);
4871 }
4872
4873 // data is changing and change not vetoed: then write it out under the lock
4874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4875
4876 if (isSnapshotMachine())
4877 {
4878 HRESULT rc = checkStateDependency(MutableStateDep);
4879 if (FAILED(rc)) return rc;
4880 }
4881
4882 if (strValue.isEmpty())
4883 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4884 else
4885 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4886 // creates a new key if needed
4887
4888 bool fNeedsGlobalSaveSettings = false;
4889 saveSettings(&fNeedsGlobalSaveSettings);
4890
4891 if (fNeedsGlobalSaveSettings)
4892 {
4893 // save the global settings; for that we should hold only the VirtualBox lock
4894 alock.release();
4895 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4896 mParent->saveSettings();
4897 }
4898 }
4899
4900 // fire notification outside the lock
4901 if (fChanged)
4902 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4903
4904 return S_OK;
4905}
4906
4907STDMETHODIMP Machine::SaveSettings()
4908{
4909 AutoCaller autoCaller(this);
4910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4911
4912 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4913
4914 /* when there was auto-conversion, we want to save the file even if
4915 * the VM is saved */
4916 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4917 if (FAILED(rc)) return rc;
4918
4919 /* the settings file path may never be null */
4920 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4921
4922 /* save all VM data excluding snapshots */
4923 bool fNeedsGlobalSaveSettings = false;
4924 rc = saveSettings(&fNeedsGlobalSaveSettings);
4925 mlock.release();
4926
4927 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4928 {
4929 // save the global settings; for that we should hold only the VirtualBox lock
4930 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4931 rc = mParent->saveSettings();
4932 }
4933
4934 return rc;
4935}
4936
4937STDMETHODIMP Machine::DiscardSettings()
4938{
4939 AutoCaller autoCaller(this);
4940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4941
4942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4943
4944 HRESULT rc = checkStateDependency(MutableStateDep);
4945 if (FAILED(rc)) return rc;
4946
4947 /*
4948 * during this rollback, the session will be notified if data has
4949 * been actually changed
4950 */
4951 rollback(true /* aNotify */);
4952
4953 return S_OK;
4954}
4955
4956/** @note Locks objects! */
4957STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4958 ComSafeArrayOut(IMedium*, aMedia))
4959{
4960 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4961 AutoLimitedCaller autoCaller(this);
4962 AssertComRCReturnRC(autoCaller.rc());
4963
4964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4965
4966 Guid id(getId());
4967
4968 if (mData->mSession.mState != SessionState_Unlocked)
4969 return setError(VBOX_E_INVALID_OBJECT_STATE,
4970 tr("Cannot unregister the machine '%s' while it is locked"),
4971 mUserData->s.strName.c_str());
4972
4973 // wait for state dependents to drop to zero
4974 ensureNoStateDependencies();
4975
4976 if (!mData->mAccessible)
4977 {
4978 // inaccessible maschines can only be unregistered; uninitialize ourselves
4979 // here because currently there may be no unregistered that are inaccessible
4980 // (this state combination is not supported). Note releasing the caller and
4981 // leaving the lock before calling uninit()
4982 alock.release();
4983 autoCaller.release();
4984
4985 uninit();
4986
4987 mParent->unregisterMachine(this, id);
4988 // calls VirtualBox::saveSettings()
4989
4990 return S_OK;
4991 }
4992
4993 HRESULT rc = S_OK;
4994
4995 // discard saved state
4996 if (mData->mMachineState == MachineState_Saved)
4997 {
4998 // add the saved state file to the list of files the caller should delete
4999 Assert(!mSSData->strStateFilePath.isEmpty());
5000 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5001
5002 mSSData->strStateFilePath.setNull();
5003
5004 // unconditionally set the machine state to powered off, we now
5005 // know no session has locked the machine
5006 mData->mMachineState = MachineState_PoweredOff;
5007 }
5008
5009 size_t cSnapshots = 0;
5010 if (mData->mFirstSnapshot)
5011 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5012 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5013 // fail now before we start detaching media
5014 return setError(VBOX_E_INVALID_OBJECT_STATE,
5015 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5016 mUserData->s.strName.c_str(), cSnapshots);
5017
5018 // This list collects the medium objects from all medium attachments
5019 // which we will detach from the machine and its snapshots, in a specific
5020 // order which allows for closing all media without getting "media in use"
5021 // errors, simply by going through the list from the front to the back:
5022 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5023 // and must be closed before the parent media from the snapshots, or closing the parents
5024 // will fail because they still have children);
5025 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5026 // the root ("first") snapshot of the machine.
5027 MediaList llMedia;
5028
5029 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5030 && mMediaData->mAttachments.size()
5031 )
5032 {
5033 // we have media attachments: detach them all and add the Medium objects to our list
5034 if (cleanupMode != CleanupMode_UnregisterOnly)
5035 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5036 else
5037 return setError(VBOX_E_INVALID_OBJECT_STATE,
5038 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5039 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5040 }
5041
5042 if (cSnapshots)
5043 {
5044 // autoCleanup must be true here, or we would have failed above
5045
5046 // add the media from the medium attachments of the snapshots to llMedia
5047 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5048 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5049 // into the children first
5050
5051 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5052 MachineState_T oldState = mData->mMachineState;
5053 mData->mMachineState = MachineState_DeletingSnapshot;
5054
5055 // make a copy of the first snapshot so the refcount does not drop to 0
5056 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5057 // because of the AutoCaller voodoo)
5058 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5059
5060 // GO!
5061 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5062
5063 mData->mMachineState = oldState;
5064 }
5065
5066 if (FAILED(rc))
5067 {
5068 rollbackMedia();
5069 return rc;
5070 }
5071
5072 // commit all the media changes made above
5073 commitMedia();
5074
5075 mData->mRegistered = false;
5076
5077 // machine lock no longer needed
5078 alock.release();
5079
5080 // return media to caller
5081 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5082 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5083
5084 mParent->unregisterMachine(this, id);
5085 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5086
5087 return S_OK;
5088}
5089
5090struct Machine::DeleteTask
5091{
5092 ComObjPtr<Machine> pMachine;
5093 RTCList<ComPtr<IMedium> > llMediums;
5094 StringsList llFilesToDelete;
5095 ComObjPtr<Progress> pProgress;
5096};
5097
5098STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5099{
5100 LogFlowFuncEnter();
5101
5102 AutoCaller autoCaller(this);
5103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5104
5105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5106
5107 HRESULT rc = checkStateDependency(MutableStateDep);
5108 if (FAILED(rc)) return rc;
5109
5110 if (mData->mRegistered)
5111 return setError(VBOX_E_INVALID_VM_STATE,
5112 tr("Cannot delete settings of a registered machine"));
5113
5114 DeleteTask *pTask = new DeleteTask;
5115 pTask->pMachine = this;
5116 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5117
5118 // collect files to delete
5119 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5120
5121 for (size_t i = 0; i < sfaMedia.size(); ++i)
5122 {
5123 IMedium *pIMedium(sfaMedia[i]);
5124 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5125 if (pMedium.isNull())
5126 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5127 SafeArray<BSTR> ids;
5128 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5129 if (FAILED(rc)) return rc;
5130 /* At this point the medium should not have any back references
5131 * anymore. If it has it is attached to another VM and *must* not
5132 * deleted. */
5133 if (ids.size() < 1)
5134 pTask->llMediums.append(pMedium);
5135 }
5136 if (mData->pMachineConfigFile->fileExists())
5137 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5138
5139 pTask->pProgress.createObject();
5140 pTask->pProgress->init(getVirtualBox(),
5141 static_cast<IMachine*>(this) /* aInitiator */,
5142 Bstr(tr("Deleting files")).raw(),
5143 true /* fCancellable */,
5144 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5145 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5146
5147 int vrc = RTThreadCreate(NULL,
5148 Machine::deleteThread,
5149 (void*)pTask,
5150 0,
5151 RTTHREADTYPE_MAIN_WORKER,
5152 0,
5153 "MachineDelete");
5154
5155 pTask->pProgress.queryInterfaceTo(aProgress);
5156
5157 if (RT_FAILURE(vrc))
5158 {
5159 delete pTask;
5160 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5161 }
5162
5163 LogFlowFuncLeave();
5164
5165 return S_OK;
5166}
5167
5168/**
5169 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5170 * calls Machine::deleteTaskWorker() on the actual machine object.
5171 * @param Thread
5172 * @param pvUser
5173 * @return
5174 */
5175/*static*/
5176DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5177{
5178 LogFlowFuncEnter();
5179
5180 DeleteTask *pTask = (DeleteTask*)pvUser;
5181 Assert(pTask);
5182 Assert(pTask->pMachine);
5183 Assert(pTask->pProgress);
5184
5185 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5186 pTask->pProgress->notifyComplete(rc);
5187
5188 delete pTask;
5189
5190 LogFlowFuncLeave();
5191
5192 NOREF(Thread);
5193
5194 return VINF_SUCCESS;
5195}
5196
5197/**
5198 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5199 * @param task
5200 * @return
5201 */
5202HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5203{
5204 AutoCaller autoCaller(this);
5205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5206
5207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5208
5209 HRESULT rc = S_OK;
5210
5211 try
5212 {
5213 ULONG uLogHistoryCount = 3;
5214 ComPtr<ISystemProperties> systemProperties;
5215 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5216 if (FAILED(rc)) throw rc;
5217
5218 if (!systemProperties.isNull())
5219 {
5220 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5221 if (FAILED(rc)) throw rc;
5222 }
5223
5224 MachineState_T oldState = mData->mMachineState;
5225 setMachineState(MachineState_SettingUp);
5226 alock.release();
5227 for (size_t i = 0; i < task.llMediums.size(); ++i)
5228 {
5229 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5230 {
5231 AutoCaller mac(pMedium);
5232 if (FAILED(mac.rc())) throw mac.rc();
5233 Utf8Str strLocation = pMedium->getLocationFull();
5234 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5235 if (FAILED(rc)) throw rc;
5236 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5237 }
5238 ComPtr<IProgress> pProgress2;
5239 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5240 if (FAILED(rc)) throw rc;
5241 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5242 if (FAILED(rc)) throw rc;
5243 /* Check the result of the asynchrony process. */
5244 LONG iRc;
5245 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5246 if (FAILED(rc)) throw rc;
5247 /* If the thread of the progress object has an error, then
5248 * retrieve the error info from there, or it'll be lost. */
5249 if (FAILED(iRc))
5250 throw setError(ProgressErrorInfo(pProgress2));
5251 }
5252 setMachineState(oldState);
5253 alock.acquire();
5254
5255 // delete the files pushed on the task list by Machine::Delete()
5256 // (this includes saved states of the machine and snapshots and
5257 // medium storage files from the IMedium list passed in, and the
5258 // machine XML file)
5259 StringsList::const_iterator it = task.llFilesToDelete.begin();
5260 while (it != task.llFilesToDelete.end())
5261 {
5262 const Utf8Str &strFile = *it;
5263 LogFunc(("Deleting file %s\n", strFile.c_str()));
5264 int vrc = RTFileDelete(strFile.c_str());
5265 if (RT_FAILURE(vrc))
5266 throw setError(VBOX_E_IPRT_ERROR,
5267 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5268
5269 ++it;
5270 if (it == task.llFilesToDelete.end())
5271 {
5272 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5273 if (FAILED(rc)) throw rc;
5274 break;
5275 }
5276
5277 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5278 if (FAILED(rc)) throw rc;
5279 }
5280
5281 /* delete the settings only when the file actually exists */
5282 if (mData->pMachineConfigFile->fileExists())
5283 {
5284 /* Delete any backup or uncommitted XML files. Ignore failures.
5285 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5286 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5287 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5288 RTFileDelete(otherXml.c_str());
5289 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5290 RTFileDelete(otherXml.c_str());
5291
5292 /* delete the Logs folder, nothing important should be left
5293 * there (we don't check for errors because the user might have
5294 * some private files there that we don't want to delete) */
5295 Utf8Str logFolder;
5296 getLogFolder(logFolder);
5297 Assert(logFolder.length());
5298 if (RTDirExists(logFolder.c_str()))
5299 {
5300 /* Delete all VBox.log[.N] files from the Logs folder
5301 * (this must be in sync with the rotation logic in
5302 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5303 * files that may have been created by the GUI. */
5304 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5305 logFolder.c_str(), RTPATH_DELIMITER);
5306 RTFileDelete(log.c_str());
5307 log = Utf8StrFmt("%s%cVBox.png",
5308 logFolder.c_str(), RTPATH_DELIMITER);
5309 RTFileDelete(log.c_str());
5310 for (int i = uLogHistoryCount; i > 0; i--)
5311 {
5312 log = Utf8StrFmt("%s%cVBox.log.%d",
5313 logFolder.c_str(), RTPATH_DELIMITER, i);
5314 RTFileDelete(log.c_str());
5315 log = Utf8StrFmt("%s%cVBox.png.%d",
5316 logFolder.c_str(), RTPATH_DELIMITER, i);
5317 RTFileDelete(log.c_str());
5318 }
5319
5320 RTDirRemove(logFolder.c_str());
5321 }
5322
5323 /* delete the Snapshots folder, nothing important should be left
5324 * there (we don't check for errors because the user might have
5325 * some private files there that we don't want to delete) */
5326 Utf8Str strFullSnapshotFolder;
5327 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5328 Assert(!strFullSnapshotFolder.isEmpty());
5329 if (RTDirExists(strFullSnapshotFolder.c_str()))
5330 RTDirRemove(strFullSnapshotFolder.c_str());
5331
5332 // delete the directory that contains the settings file, but only
5333 // if it matches the VM name
5334 Utf8Str settingsDir;
5335 if (isInOwnDir(&settingsDir))
5336 RTDirRemove(settingsDir.c_str());
5337 }
5338
5339 alock.release();
5340
5341 mParent->saveModifiedRegistries();
5342 }
5343 catch (HRESULT aRC) { rc = aRC; }
5344
5345 return rc;
5346}
5347
5348STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5349{
5350 CheckComArgOutPointerValid(aSnapshot);
5351
5352 AutoCaller autoCaller(this);
5353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5354
5355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5356
5357 ComObjPtr<Snapshot> pSnapshot;
5358 HRESULT rc;
5359
5360 if (!aNameOrId || !*aNameOrId)
5361 // null case (caller wants root snapshot): findSnapshotById() handles this
5362 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5363 else
5364 {
5365 Guid uuid(aNameOrId);
5366 if (uuid.isValid())
5367 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5368 else
5369 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5370 }
5371 pSnapshot.queryInterfaceTo(aSnapshot);
5372
5373 return rc;
5374}
5375
5376STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5377{
5378 CheckComArgStrNotEmptyOrNull(aName);
5379 CheckComArgStrNotEmptyOrNull(aHostPath);
5380
5381 AutoCaller autoCaller(this);
5382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5383
5384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5385
5386 HRESULT rc = checkStateDependency(MutableStateDep);
5387 if (FAILED(rc)) return rc;
5388
5389 Utf8Str strName(aName);
5390
5391 ComObjPtr<SharedFolder> sharedFolder;
5392 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5393 if (SUCCEEDED(rc))
5394 return setError(VBOX_E_OBJECT_IN_USE,
5395 tr("Shared folder named '%s' already exists"),
5396 strName.c_str());
5397
5398 sharedFolder.createObject();
5399 rc = sharedFolder->init(getMachine(),
5400 strName,
5401 aHostPath,
5402 !!aWritable,
5403 !!aAutoMount,
5404 true /* fFailOnError */);
5405 if (FAILED(rc)) return rc;
5406
5407 setModified(IsModified_SharedFolders);
5408 mHWData.backup();
5409 mHWData->mSharedFolders.push_back(sharedFolder);
5410
5411 /* inform the direct session if any */
5412 alock.release();
5413 onSharedFolderChange();
5414
5415 return S_OK;
5416}
5417
5418STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5419{
5420 CheckComArgStrNotEmptyOrNull(aName);
5421
5422 AutoCaller autoCaller(this);
5423 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5424
5425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5426
5427 HRESULT rc = checkStateDependency(MutableStateDep);
5428 if (FAILED(rc)) return rc;
5429
5430 ComObjPtr<SharedFolder> sharedFolder;
5431 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5432 if (FAILED(rc)) return rc;
5433
5434 setModified(IsModified_SharedFolders);
5435 mHWData.backup();
5436 mHWData->mSharedFolders.remove(sharedFolder);
5437
5438 /* inform the direct session if any */
5439 alock.release();
5440 onSharedFolderChange();
5441
5442 return S_OK;
5443}
5444
5445STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5446{
5447 CheckComArgOutPointerValid(aCanShow);
5448
5449 /* start with No */
5450 *aCanShow = FALSE;
5451
5452 AutoCaller autoCaller(this);
5453 AssertComRCReturnRC(autoCaller.rc());
5454
5455 ComPtr<IInternalSessionControl> directControl;
5456 {
5457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5458
5459 if (mData->mSession.mState != SessionState_Locked)
5460 return setError(VBOX_E_INVALID_VM_STATE,
5461 tr("Machine is not locked for session (session state: %s)"),
5462 Global::stringifySessionState(mData->mSession.mState));
5463
5464 directControl = mData->mSession.mDirectControl;
5465 }
5466
5467 /* ignore calls made after #OnSessionEnd() is called */
5468 if (!directControl)
5469 return S_OK;
5470
5471 LONG64 dummy;
5472 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5473}
5474
5475STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5476{
5477 CheckComArgOutPointerValid(aWinId);
5478
5479 AutoCaller autoCaller(this);
5480 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5481
5482 ComPtr<IInternalSessionControl> directControl;
5483 {
5484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5485
5486 if (mData->mSession.mState != SessionState_Locked)
5487 return setError(E_FAIL,
5488 tr("Machine is not locked for session (session state: %s)"),
5489 Global::stringifySessionState(mData->mSession.mState));
5490
5491 directControl = mData->mSession.mDirectControl;
5492 }
5493
5494 /* ignore calls made after #OnSessionEnd() is called */
5495 if (!directControl)
5496 return S_OK;
5497
5498 BOOL dummy;
5499 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5500}
5501
5502#ifdef VBOX_WITH_GUEST_PROPS
5503/**
5504 * Look up a guest property in VBoxSVC's internal structures.
5505 */
5506HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5507 BSTR *aValue,
5508 LONG64 *aTimestamp,
5509 BSTR *aFlags) const
5510{
5511 using namespace guestProp;
5512
5513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5514 Utf8Str strName(aName);
5515 HWData::GuestPropertyMap::const_iterator it =
5516 mHWData->mGuestProperties.find(strName);
5517
5518 if (it != mHWData->mGuestProperties.end())
5519 {
5520 char szFlags[MAX_FLAGS_LEN + 1];
5521 it->second.strValue.cloneTo(aValue);
5522 *aTimestamp = it->second.mTimestamp;
5523 writeFlags(it->second.mFlags, szFlags);
5524 Bstr(szFlags).cloneTo(aFlags);
5525 }
5526
5527 return S_OK;
5528}
5529
5530/**
5531 * Query the VM that a guest property belongs to for the property.
5532 * @returns E_ACCESSDENIED if the VM process is not available or not
5533 * currently handling queries and the lookup should then be done in
5534 * VBoxSVC.
5535 */
5536HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5537 BSTR *aValue,
5538 LONG64 *aTimestamp,
5539 BSTR *aFlags) const
5540{
5541 HRESULT rc;
5542 ComPtr<IInternalSessionControl> directControl;
5543 directControl = mData->mSession.mDirectControl;
5544
5545 /* fail if we were called after #OnSessionEnd() is called. This is a
5546 * silly race condition. */
5547
5548 if (!directControl)
5549 rc = E_ACCESSDENIED;
5550 else
5551 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5552 false /* isSetter */,
5553 aValue, aTimestamp, aFlags);
5554 return rc;
5555}
5556#endif // VBOX_WITH_GUEST_PROPS
5557
5558STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5559 BSTR *aValue,
5560 LONG64 *aTimestamp,
5561 BSTR *aFlags)
5562{
5563#ifndef VBOX_WITH_GUEST_PROPS
5564 ReturnComNotImplemented();
5565#else // VBOX_WITH_GUEST_PROPS
5566 CheckComArgStrNotEmptyOrNull(aName);
5567 CheckComArgOutPointerValid(aValue);
5568 CheckComArgOutPointerValid(aTimestamp);
5569 CheckComArgOutPointerValid(aFlags);
5570
5571 AutoCaller autoCaller(this);
5572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5573
5574 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5575 if (rc == E_ACCESSDENIED)
5576 /* The VM is not running or the service is not (yet) accessible */
5577 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5578 return rc;
5579#endif // VBOX_WITH_GUEST_PROPS
5580}
5581
5582STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5583{
5584 LONG64 dummyTimestamp;
5585 Bstr dummyFlags;
5586 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5587}
5588
5589STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5590{
5591 Bstr dummyValue;
5592 Bstr dummyFlags;
5593 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5594}
5595
5596#ifdef VBOX_WITH_GUEST_PROPS
5597/**
5598 * Set a guest property in VBoxSVC's internal structures.
5599 */
5600HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5601 IN_BSTR aFlags)
5602{
5603 using namespace guestProp;
5604
5605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5606 HRESULT rc = S_OK;
5607 HWData::GuestProperty property;
5608 property.mFlags = NILFLAG;
5609
5610 rc = checkStateDependency(MutableStateDep);
5611 if (FAILED(rc)) return rc;
5612
5613 try
5614 {
5615 Utf8Str utf8Name(aName);
5616 Utf8Str utf8Flags(aFlags);
5617 uint32_t fFlags = NILFLAG;
5618 if ( (aFlags != NULL)
5619 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5620 )
5621 return setError(E_INVALIDARG,
5622 tr("Invalid guest property flag values: '%ls'"),
5623 aFlags);
5624
5625 HWData::GuestPropertyMap::iterator it =
5626 mHWData->mGuestProperties.find(utf8Name);
5627
5628 if (it == mHWData->mGuestProperties.end())
5629 {
5630 setModified(IsModified_MachineData);
5631 mHWData.backupEx();
5632
5633 RTTIMESPEC time;
5634 HWData::GuestProperty prop;
5635 prop.strValue = aValue;
5636 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5637 prop.mFlags = fFlags;
5638
5639 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5640 }
5641 else
5642 {
5643 if (it->second.mFlags & (RDONLYHOST))
5644 {
5645 rc = setError(E_ACCESSDENIED,
5646 tr("The property '%ls' cannot be changed by the host"),
5647 aName);
5648 }
5649 else
5650 {
5651 setModified(IsModified_MachineData);
5652 mHWData.backupEx();
5653
5654 /* The backupEx() operation invalidates our iterator,
5655 * so get a new one. */
5656 it = mHWData->mGuestProperties.find(utf8Name);
5657 Assert(it != mHWData->mGuestProperties.end());
5658
5659 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5660 {
5661 RTTIMESPEC time;
5662 it->second.strValue = aValue;
5663 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5664 if (aFlags != NULL)
5665 it->second.mFlags = fFlags;
5666 }
5667 else
5668 {
5669 mHWData->mGuestProperties.erase(it);
5670 }
5671 }
5672 }
5673
5674 if ( SUCCEEDED(rc)
5675 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5676 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5677 RTSTR_MAX,
5678 utf8Name.c_str(),
5679 RTSTR_MAX,
5680 NULL)
5681 )
5682 )
5683 {
5684 alock.release();
5685
5686 mParent->onGuestPropertyChange(mData->mUuid, aName,
5687 aValue ? aValue : Bstr("").raw(),
5688 aFlags ? aFlags : Bstr("").raw());
5689 }
5690 }
5691 catch (std::bad_alloc &)
5692 {
5693 rc = E_OUTOFMEMORY;
5694 }
5695
5696 return rc;
5697}
5698
5699/**
5700 * Set a property on the VM that that property belongs to.
5701 * @returns E_ACCESSDENIED if the VM process is not available or not
5702 * currently handling queries and the setting should then be done in
5703 * VBoxSVC.
5704 */
5705HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5706 IN_BSTR aFlags)
5707{
5708 HRESULT rc;
5709
5710 try
5711 {
5712 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5713
5714 BSTR dummy = NULL; /* will not be changed (setter) */
5715 LONG64 dummy64;
5716 if (!directControl)
5717 rc = E_ACCESSDENIED;
5718 else
5719 /** @todo Fix when adding DeleteGuestProperty(),
5720 see defect. */
5721 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5722 true /* isSetter */,
5723 &dummy, &dummy64, &dummy);
5724 }
5725 catch (std::bad_alloc &)
5726 {
5727 rc = E_OUTOFMEMORY;
5728 }
5729
5730 return rc;
5731}
5732#endif // VBOX_WITH_GUEST_PROPS
5733
5734STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5735 IN_BSTR aFlags)
5736{
5737#ifndef VBOX_WITH_GUEST_PROPS
5738 ReturnComNotImplemented();
5739#else // VBOX_WITH_GUEST_PROPS
5740 CheckComArgStrNotEmptyOrNull(aName);
5741 CheckComArgMaybeNull(aFlags);
5742 CheckComArgMaybeNull(aValue);
5743
5744 AutoCaller autoCaller(this);
5745 if (FAILED(autoCaller.rc()))
5746 return autoCaller.rc();
5747
5748 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5749 if (rc == E_ACCESSDENIED)
5750 /* The VM is not running or the service is not (yet) accessible */
5751 rc = setGuestPropertyToService(aName, aValue, aFlags);
5752 return rc;
5753#endif // VBOX_WITH_GUEST_PROPS
5754}
5755
5756STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5757{
5758 return SetGuestProperty(aName, aValue, NULL);
5759}
5760
5761STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5762{
5763 return SetGuestProperty(aName, NULL, NULL);
5764}
5765
5766#ifdef VBOX_WITH_GUEST_PROPS
5767/**
5768 * Enumerate the guest properties in VBoxSVC's internal structures.
5769 */
5770HRESULT Machine::enumerateGuestPropertiesInService
5771 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5772 ComSafeArrayOut(BSTR, aValues),
5773 ComSafeArrayOut(LONG64, aTimestamps),
5774 ComSafeArrayOut(BSTR, aFlags))
5775{
5776 using namespace guestProp;
5777
5778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5779 Utf8Str strPatterns(aPatterns);
5780
5781 HWData::GuestPropertyMap propMap;
5782
5783 /*
5784 * Look for matching patterns and build up a list.
5785 */
5786 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5787 while (it != mHWData->mGuestProperties.end())
5788 {
5789 if ( strPatterns.isEmpty()
5790 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5791 RTSTR_MAX,
5792 it->first.c_str(),
5793 RTSTR_MAX,
5794 NULL)
5795 )
5796 {
5797 propMap.insert(*it);
5798 }
5799
5800 it++;
5801 }
5802
5803 alock.release();
5804
5805 /*
5806 * And build up the arrays for returning the property information.
5807 */
5808 size_t cEntries = propMap.size();
5809 SafeArray<BSTR> names(cEntries);
5810 SafeArray<BSTR> values(cEntries);
5811 SafeArray<LONG64> timestamps(cEntries);
5812 SafeArray<BSTR> flags(cEntries);
5813 size_t iProp = 0;
5814
5815 it = propMap.begin();
5816 while (it != propMap.end())
5817 {
5818 char szFlags[MAX_FLAGS_LEN + 1];
5819 it->first.cloneTo(&names[iProp]);
5820 it->second.strValue.cloneTo(&values[iProp]);
5821 timestamps[iProp] = it->second.mTimestamp;
5822 writeFlags(it->second.mFlags, szFlags);
5823 Bstr(szFlags).cloneTo(&flags[iProp++]);
5824 it++;
5825 }
5826 names.detachTo(ComSafeArrayOutArg(aNames));
5827 values.detachTo(ComSafeArrayOutArg(aValues));
5828 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5829 flags.detachTo(ComSafeArrayOutArg(aFlags));
5830 return S_OK;
5831}
5832
5833/**
5834 * Enumerate the properties managed by a VM.
5835 * @returns E_ACCESSDENIED if the VM process is not available or not
5836 * currently handling queries and the setting should then be done in
5837 * VBoxSVC.
5838 */
5839HRESULT Machine::enumerateGuestPropertiesOnVM
5840 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5841 ComSafeArrayOut(BSTR, aValues),
5842 ComSafeArrayOut(LONG64, aTimestamps),
5843 ComSafeArrayOut(BSTR, aFlags))
5844{
5845 HRESULT rc;
5846 ComPtr<IInternalSessionControl> directControl;
5847 directControl = mData->mSession.mDirectControl;
5848
5849 if (!directControl)
5850 rc = E_ACCESSDENIED;
5851 else
5852 rc = directControl->EnumerateGuestProperties
5853 (aPatterns, ComSafeArrayOutArg(aNames),
5854 ComSafeArrayOutArg(aValues),
5855 ComSafeArrayOutArg(aTimestamps),
5856 ComSafeArrayOutArg(aFlags));
5857 return rc;
5858}
5859#endif // VBOX_WITH_GUEST_PROPS
5860
5861STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5862 ComSafeArrayOut(BSTR, aNames),
5863 ComSafeArrayOut(BSTR, aValues),
5864 ComSafeArrayOut(LONG64, aTimestamps),
5865 ComSafeArrayOut(BSTR, aFlags))
5866{
5867#ifndef VBOX_WITH_GUEST_PROPS
5868 ReturnComNotImplemented();
5869#else // VBOX_WITH_GUEST_PROPS
5870 CheckComArgMaybeNull(aPatterns);
5871 CheckComArgOutSafeArrayPointerValid(aNames);
5872 CheckComArgOutSafeArrayPointerValid(aValues);
5873 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5874 CheckComArgOutSafeArrayPointerValid(aFlags);
5875
5876 AutoCaller autoCaller(this);
5877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5878
5879 HRESULT rc = enumerateGuestPropertiesOnVM
5880 (aPatterns, ComSafeArrayOutArg(aNames),
5881 ComSafeArrayOutArg(aValues),
5882 ComSafeArrayOutArg(aTimestamps),
5883 ComSafeArrayOutArg(aFlags));
5884 if (rc == E_ACCESSDENIED)
5885 /* The VM is not running or the service is not (yet) accessible */
5886 rc = enumerateGuestPropertiesInService
5887 (aPatterns, ComSafeArrayOutArg(aNames),
5888 ComSafeArrayOutArg(aValues),
5889 ComSafeArrayOutArg(aTimestamps),
5890 ComSafeArrayOutArg(aFlags));
5891 return rc;
5892#endif // VBOX_WITH_GUEST_PROPS
5893}
5894
5895STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5896 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5897{
5898 MediaData::AttachmentList atts;
5899
5900 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5901 if (FAILED(rc)) return rc;
5902
5903 SafeIfaceArray<IMediumAttachment> attachments(atts);
5904 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5905
5906 return S_OK;
5907}
5908
5909STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5910 LONG aControllerPort,
5911 LONG aDevice,
5912 IMediumAttachment **aAttachment)
5913{
5914 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5915 aControllerName, aControllerPort, aDevice));
5916
5917 CheckComArgStrNotEmptyOrNull(aControllerName);
5918 CheckComArgOutPointerValid(aAttachment);
5919
5920 AutoCaller autoCaller(this);
5921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5922
5923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5924
5925 *aAttachment = NULL;
5926
5927 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5928 aControllerName,
5929 aControllerPort,
5930 aDevice);
5931 if (pAttach.isNull())
5932 return setError(VBOX_E_OBJECT_NOT_FOUND,
5933 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5934 aDevice, aControllerPort, aControllerName);
5935
5936 pAttach.queryInterfaceTo(aAttachment);
5937
5938 return S_OK;
5939}
5940
5941STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5942 StorageBus_T aConnectionType,
5943 IStorageController **controller)
5944{
5945 CheckComArgStrNotEmptyOrNull(aName);
5946
5947 if ( (aConnectionType <= StorageBus_Null)
5948 || (aConnectionType > StorageBus_SAS))
5949 return setError(E_INVALIDARG,
5950 tr("Invalid connection type: %d"),
5951 aConnectionType);
5952
5953 AutoCaller autoCaller(this);
5954 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5955
5956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5957
5958 HRESULT rc = checkStateDependency(MutableStateDep);
5959 if (FAILED(rc)) return rc;
5960
5961 /* try to find one with the name first. */
5962 ComObjPtr<StorageController> ctrl;
5963
5964 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5965 if (SUCCEEDED(rc))
5966 return setError(VBOX_E_OBJECT_IN_USE,
5967 tr("Storage controller named '%ls' already exists"),
5968 aName);
5969
5970 ctrl.createObject();
5971
5972 /* get a new instance number for the storage controller */
5973 ULONG ulInstance = 0;
5974 bool fBootable = true;
5975 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5976 it != mStorageControllers->end();
5977 ++it)
5978 {
5979 if ((*it)->getStorageBus() == aConnectionType)
5980 {
5981 ULONG ulCurInst = (*it)->getInstance();
5982
5983 if (ulCurInst >= ulInstance)
5984 ulInstance = ulCurInst + 1;
5985
5986 /* Only one controller of each type can be marked as bootable. */
5987 if ((*it)->getBootable())
5988 fBootable = false;
5989 }
5990 }
5991
5992 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5993 if (FAILED(rc)) return rc;
5994
5995 setModified(IsModified_Storage);
5996 mStorageControllers.backup();
5997 mStorageControllers->push_back(ctrl);
5998
5999 ctrl.queryInterfaceTo(controller);
6000
6001 /* inform the direct session if any */
6002 alock.release();
6003 onStorageControllerChange();
6004
6005 return S_OK;
6006}
6007
6008STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6009 IStorageController **aStorageController)
6010{
6011 CheckComArgStrNotEmptyOrNull(aName);
6012
6013 AutoCaller autoCaller(this);
6014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6015
6016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6017
6018 ComObjPtr<StorageController> ctrl;
6019
6020 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6021 if (SUCCEEDED(rc))
6022 ctrl.queryInterfaceTo(aStorageController);
6023
6024 return rc;
6025}
6026
6027STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6028 IStorageController **aStorageController)
6029{
6030 AutoCaller autoCaller(this);
6031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6032
6033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6034
6035 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6036 it != mStorageControllers->end();
6037 ++it)
6038 {
6039 if ((*it)->getInstance() == aInstance)
6040 {
6041 (*it).queryInterfaceTo(aStorageController);
6042 return S_OK;
6043 }
6044 }
6045
6046 return setError(VBOX_E_OBJECT_NOT_FOUND,
6047 tr("Could not find a storage controller with instance number '%lu'"),
6048 aInstance);
6049}
6050
6051STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6052{
6053 AutoCaller autoCaller(this);
6054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6055
6056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6057
6058 HRESULT rc = checkStateDependency(MutableStateDep);
6059 if (FAILED(rc)) return rc;
6060
6061 ComObjPtr<StorageController> ctrl;
6062
6063 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6064 if (SUCCEEDED(rc))
6065 {
6066 /* Ensure that only one controller of each type is marked as bootable. */
6067 if (fBootable == TRUE)
6068 {
6069 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6070 it != mStorageControllers->end();
6071 ++it)
6072 {
6073 ComObjPtr<StorageController> aCtrl = (*it);
6074
6075 if ( (aCtrl->getName() != Utf8Str(aName))
6076 && aCtrl->getBootable() == TRUE
6077 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6078 && aCtrl->getControllerType() == ctrl->getControllerType())
6079 {
6080 aCtrl->setBootable(FALSE);
6081 break;
6082 }
6083 }
6084 }
6085
6086 if (SUCCEEDED(rc))
6087 {
6088 ctrl->setBootable(fBootable);
6089 setModified(IsModified_Storage);
6090 }
6091 }
6092
6093 if (SUCCEEDED(rc))
6094 {
6095 /* inform the direct session if any */
6096 alock.release();
6097 onStorageControllerChange();
6098 }
6099
6100 return rc;
6101}
6102
6103STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6104{
6105 CheckComArgStrNotEmptyOrNull(aName);
6106
6107 AutoCaller autoCaller(this);
6108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6109
6110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6111
6112 HRESULT rc = checkStateDependency(MutableStateDep);
6113 if (FAILED(rc)) return rc;
6114
6115 ComObjPtr<StorageController> ctrl;
6116 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6117 if (FAILED(rc)) return rc;
6118
6119 {
6120 /* find all attached devices to the appropriate storage controller and detach them all */
6121 // make a temporary list because detachDevice invalidates iterators into
6122 // mMediaData->mAttachments
6123 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6124
6125 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6126 it != llAttachments2.end();
6127 ++it)
6128 {
6129 MediumAttachment *pAttachTemp = *it;
6130
6131 AutoCaller localAutoCaller(pAttachTemp);
6132 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6133
6134 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6135
6136 if (pAttachTemp->getControllerName() == aName)
6137 {
6138 rc = detachDevice(pAttachTemp, alock, NULL);
6139 if (FAILED(rc)) return rc;
6140 }
6141 }
6142 }
6143
6144 /* We can remove it now. */
6145 setModified(IsModified_Storage);
6146 mStorageControllers.backup();
6147
6148 ctrl->unshare();
6149
6150 mStorageControllers->remove(ctrl);
6151
6152 /* inform the direct session if any */
6153 alock.release();
6154 onStorageControllerChange();
6155
6156 return S_OK;
6157}
6158
6159STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6160 ULONG *puOriginX,
6161 ULONG *puOriginY,
6162 ULONG *puWidth,
6163 ULONG *puHeight,
6164 BOOL *pfEnabled)
6165{
6166 LogFlowThisFunc(("\n"));
6167
6168 CheckComArgNotNull(puOriginX);
6169 CheckComArgNotNull(puOriginY);
6170 CheckComArgNotNull(puWidth);
6171 CheckComArgNotNull(puHeight);
6172 CheckComArgNotNull(pfEnabled);
6173
6174 uint32_t u32OriginX= 0;
6175 uint32_t u32OriginY= 0;
6176 uint32_t u32Width = 0;
6177 uint32_t u32Height = 0;
6178 uint16_t u16Flags = 0;
6179
6180 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6181 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6182 if (RT_FAILURE(vrc))
6183 {
6184#ifdef RT_OS_WINDOWS
6185 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6186 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6187 * So just assign fEnable to TRUE again.
6188 * The right fix would be to change GUI API wrappers to make sure that parameters
6189 * are changed only if API succeeds.
6190 */
6191 *pfEnabled = TRUE;
6192#endif
6193 return setError(VBOX_E_IPRT_ERROR,
6194 tr("Saved guest size is not available (%Rrc)"),
6195 vrc);
6196 }
6197
6198 *puOriginX = u32OriginX;
6199 *puOriginY = u32OriginY;
6200 *puWidth = u32Width;
6201 *puHeight = u32Height;
6202 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6203
6204 return S_OK;
6205}
6206
6207STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6208{
6209 LogFlowThisFunc(("\n"));
6210
6211 CheckComArgNotNull(aSize);
6212 CheckComArgNotNull(aWidth);
6213 CheckComArgNotNull(aHeight);
6214
6215 if (aScreenId != 0)
6216 return E_NOTIMPL;
6217
6218 AutoCaller autoCaller(this);
6219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6220
6221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6222
6223 uint8_t *pu8Data = NULL;
6224 uint32_t cbData = 0;
6225 uint32_t u32Width = 0;
6226 uint32_t u32Height = 0;
6227
6228 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6229
6230 if (RT_FAILURE(vrc))
6231 return setError(VBOX_E_IPRT_ERROR,
6232 tr("Saved screenshot data is not available (%Rrc)"),
6233 vrc);
6234
6235 *aSize = cbData;
6236 *aWidth = u32Width;
6237 *aHeight = u32Height;
6238
6239 freeSavedDisplayScreenshot(pu8Data);
6240
6241 return S_OK;
6242}
6243
6244STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6245{
6246 LogFlowThisFunc(("\n"));
6247
6248 CheckComArgNotNull(aWidth);
6249 CheckComArgNotNull(aHeight);
6250 CheckComArgOutSafeArrayPointerValid(aData);
6251
6252 if (aScreenId != 0)
6253 return E_NOTIMPL;
6254
6255 AutoCaller autoCaller(this);
6256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6257
6258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
6260 uint8_t *pu8Data = NULL;
6261 uint32_t cbData = 0;
6262 uint32_t u32Width = 0;
6263 uint32_t u32Height = 0;
6264
6265 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6266
6267 if (RT_FAILURE(vrc))
6268 return setError(VBOX_E_IPRT_ERROR,
6269 tr("Saved screenshot data is not available (%Rrc)"),
6270 vrc);
6271
6272 *aWidth = u32Width;
6273 *aHeight = u32Height;
6274
6275 com::SafeArray<BYTE> bitmap(cbData);
6276 /* Convert pixels to format expected by the API caller. */
6277 if (aBGR)
6278 {
6279 /* [0] B, [1] G, [2] R, [3] A. */
6280 for (unsigned i = 0; i < cbData; i += 4)
6281 {
6282 bitmap[i] = pu8Data[i];
6283 bitmap[i + 1] = pu8Data[i + 1];
6284 bitmap[i + 2] = pu8Data[i + 2];
6285 bitmap[i + 3] = 0xff;
6286 }
6287 }
6288 else
6289 {
6290 /* [0] R, [1] G, [2] B, [3] A. */
6291 for (unsigned i = 0; i < cbData; i += 4)
6292 {
6293 bitmap[i] = pu8Data[i + 2];
6294 bitmap[i + 1] = pu8Data[i + 1];
6295 bitmap[i + 2] = pu8Data[i];
6296 bitmap[i + 3] = 0xff;
6297 }
6298 }
6299 bitmap.detachTo(ComSafeArrayOutArg(aData));
6300
6301 freeSavedDisplayScreenshot(pu8Data);
6302
6303 return S_OK;
6304}
6305
6306
6307STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6308{
6309 LogFlowThisFunc(("\n"));
6310
6311 CheckComArgNotNull(aWidth);
6312 CheckComArgNotNull(aHeight);
6313 CheckComArgOutSafeArrayPointerValid(aData);
6314
6315 if (aScreenId != 0)
6316 return E_NOTIMPL;
6317
6318 AutoCaller autoCaller(this);
6319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6320
6321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 uint8_t *pu8Data = NULL;
6324 uint32_t cbData = 0;
6325 uint32_t u32Width = 0;
6326 uint32_t u32Height = 0;
6327
6328 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6329
6330 if (RT_FAILURE(vrc))
6331 return setError(VBOX_E_IPRT_ERROR,
6332 tr("Saved screenshot data is not available (%Rrc)"),
6333 vrc);
6334
6335 *aWidth = u32Width;
6336 *aHeight = u32Height;
6337
6338 HRESULT rc = S_OK;
6339 uint8_t *pu8PNG = NULL;
6340 uint32_t cbPNG = 0;
6341 uint32_t cxPNG = 0;
6342 uint32_t cyPNG = 0;
6343
6344 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6345
6346 if (RT_SUCCESS(vrc))
6347 {
6348 com::SafeArray<BYTE> screenData(cbPNG);
6349 screenData.initFrom(pu8PNG, cbPNG);
6350 if (pu8PNG)
6351 RTMemFree(pu8PNG);
6352 screenData.detachTo(ComSafeArrayOutArg(aData));
6353 }
6354 else
6355 {
6356 if (pu8PNG)
6357 RTMemFree(pu8PNG);
6358 return setError(VBOX_E_IPRT_ERROR,
6359 tr("Could not convert screenshot to PNG (%Rrc)"),
6360 vrc);
6361 }
6362
6363 freeSavedDisplayScreenshot(pu8Data);
6364
6365 return rc;
6366}
6367
6368STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6369{
6370 LogFlowThisFunc(("\n"));
6371
6372 CheckComArgNotNull(aSize);
6373 CheckComArgNotNull(aWidth);
6374 CheckComArgNotNull(aHeight);
6375
6376 if (aScreenId != 0)
6377 return E_NOTIMPL;
6378
6379 AutoCaller autoCaller(this);
6380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6381
6382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 uint8_t *pu8Data = NULL;
6385 uint32_t cbData = 0;
6386 uint32_t u32Width = 0;
6387 uint32_t u32Height = 0;
6388
6389 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6390
6391 if (RT_FAILURE(vrc))
6392 return setError(VBOX_E_IPRT_ERROR,
6393 tr("Saved screenshot data is not available (%Rrc)"),
6394 vrc);
6395
6396 *aSize = cbData;
6397 *aWidth = u32Width;
6398 *aHeight = u32Height;
6399
6400 freeSavedDisplayScreenshot(pu8Data);
6401
6402 return S_OK;
6403}
6404
6405STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6406{
6407 LogFlowThisFunc(("\n"));
6408
6409 CheckComArgNotNull(aWidth);
6410 CheckComArgNotNull(aHeight);
6411 CheckComArgOutSafeArrayPointerValid(aData);
6412
6413 if (aScreenId != 0)
6414 return E_NOTIMPL;
6415
6416 AutoCaller autoCaller(this);
6417 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6418
6419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 uint8_t *pu8Data = NULL;
6422 uint32_t cbData = 0;
6423 uint32_t u32Width = 0;
6424 uint32_t u32Height = 0;
6425
6426 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6427
6428 if (RT_FAILURE(vrc))
6429 return setError(VBOX_E_IPRT_ERROR,
6430 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6431 vrc);
6432
6433 *aWidth = u32Width;
6434 *aHeight = u32Height;
6435
6436 com::SafeArray<BYTE> png(cbData);
6437 png.initFrom(pu8Data, cbData);
6438 png.detachTo(ComSafeArrayOutArg(aData));
6439
6440 freeSavedDisplayScreenshot(pu8Data);
6441
6442 return S_OK;
6443}
6444
6445STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6446{
6447 HRESULT rc = S_OK;
6448 LogFlowThisFunc(("\n"));
6449
6450 AutoCaller autoCaller(this);
6451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6452
6453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 if (!mHWData->mCPUHotPlugEnabled)
6456 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6457
6458 if (aCpu >= mHWData->mCPUCount)
6459 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6460
6461 if (mHWData->mCPUAttached[aCpu])
6462 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6463
6464 alock.release();
6465 rc = onCPUChange(aCpu, false);
6466 alock.acquire();
6467 if (FAILED(rc)) return rc;
6468
6469 setModified(IsModified_MachineData);
6470 mHWData.backup();
6471 mHWData->mCPUAttached[aCpu] = true;
6472
6473 /* Save settings if online */
6474 if (Global::IsOnline(mData->mMachineState))
6475 saveSettings(NULL);
6476
6477 return S_OK;
6478}
6479
6480STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6481{
6482 HRESULT rc = S_OK;
6483 LogFlowThisFunc(("\n"));
6484
6485 AutoCaller autoCaller(this);
6486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6487
6488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 if (!mHWData->mCPUHotPlugEnabled)
6491 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6492
6493 if (aCpu >= SchemaDefs::MaxCPUCount)
6494 return setError(E_INVALIDARG,
6495 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6496 SchemaDefs::MaxCPUCount);
6497
6498 if (!mHWData->mCPUAttached[aCpu])
6499 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6500
6501 /* CPU 0 can't be detached */
6502 if (aCpu == 0)
6503 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6504
6505 alock.release();
6506 rc = onCPUChange(aCpu, true);
6507 alock.acquire();
6508 if (FAILED(rc)) return rc;
6509
6510 setModified(IsModified_MachineData);
6511 mHWData.backup();
6512 mHWData->mCPUAttached[aCpu] = false;
6513
6514 /* Save settings if online */
6515 if (Global::IsOnline(mData->mMachineState))
6516 saveSettings(NULL);
6517
6518 return S_OK;
6519}
6520
6521STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6522{
6523 LogFlowThisFunc(("\n"));
6524
6525 CheckComArgNotNull(aCpuAttached);
6526
6527 *aCpuAttached = false;
6528
6529 AutoCaller autoCaller(this);
6530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6531
6532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6533
6534 /* If hotplug is enabled the CPU is always enabled. */
6535 if (!mHWData->mCPUHotPlugEnabled)
6536 {
6537 if (aCpu < mHWData->mCPUCount)
6538 *aCpuAttached = true;
6539 }
6540 else
6541 {
6542 if (aCpu < SchemaDefs::MaxCPUCount)
6543 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6544 }
6545
6546 return S_OK;
6547}
6548
6549STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6550{
6551 CheckComArgOutPointerValid(aName);
6552
6553 AutoCaller autoCaller(this);
6554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6555
6556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 Utf8Str log = queryLogFilename(aIdx);
6559 if (!RTFileExists(log.c_str()))
6560 log.setNull();
6561 log.cloneTo(aName);
6562
6563 return S_OK;
6564}
6565
6566STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6567{
6568 LogFlowThisFunc(("\n"));
6569 CheckComArgOutSafeArrayPointerValid(aData);
6570 if (aSize < 0)
6571 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6572
6573 AutoCaller autoCaller(this);
6574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6575
6576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6577
6578 HRESULT rc = S_OK;
6579 Utf8Str log = queryLogFilename(aIdx);
6580
6581 /* do not unnecessarily hold the lock while doing something which does
6582 * not need the lock and potentially takes a long time. */
6583 alock.release();
6584
6585 /* Limit the chunk size to 32K for now, as that gives better performance
6586 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6587 * One byte expands to approx. 25 bytes of breathtaking XML. */
6588 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6589 com::SafeArray<BYTE> logData(cbData);
6590
6591 RTFILE LogFile;
6592 int vrc = RTFileOpen(&LogFile, log.c_str(),
6593 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6594 if (RT_SUCCESS(vrc))
6595 {
6596 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6597 if (RT_SUCCESS(vrc))
6598 logData.resize(cbData);
6599 else
6600 rc = setError(VBOX_E_IPRT_ERROR,
6601 tr("Could not read log file '%s' (%Rrc)"),
6602 log.c_str(), vrc);
6603 RTFileClose(LogFile);
6604 }
6605 else
6606 rc = setError(VBOX_E_IPRT_ERROR,
6607 tr("Could not open log file '%s' (%Rrc)"),
6608 log.c_str(), vrc);
6609
6610 if (FAILED(rc))
6611 logData.resize(0);
6612 logData.detachTo(ComSafeArrayOutArg(aData));
6613
6614 return rc;
6615}
6616
6617
6618/**
6619 * Currently this method doesn't attach device to the running VM,
6620 * just makes sure it's plugged on next VM start.
6621 */
6622STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6623{
6624 AutoCaller autoCaller(this);
6625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6626
6627 // lock scope
6628 {
6629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6630
6631 HRESULT rc = checkStateDependency(MutableStateDep);
6632 if (FAILED(rc)) return rc;
6633
6634 ChipsetType_T aChipset = ChipsetType_PIIX3;
6635 COMGETTER(ChipsetType)(&aChipset);
6636
6637 if (aChipset != ChipsetType_ICH9)
6638 {
6639 return setError(E_INVALIDARG,
6640 tr("Host PCI attachment only supported with ICH9 chipset"));
6641 }
6642
6643 // check if device with this host PCI address already attached
6644 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6645 it != mHWData->mPCIDeviceAssignments.end();
6646 ++it)
6647 {
6648 LONG iHostAddress = -1;
6649 ComPtr<PCIDeviceAttachment> pAttach;
6650 pAttach = *it;
6651 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6652 if (iHostAddress == hostAddress)
6653 return setError(E_INVALIDARG,
6654 tr("Device with host PCI address already attached to this VM"));
6655 }
6656
6657 ComObjPtr<PCIDeviceAttachment> pda;
6658 char name[32];
6659
6660 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6661 Bstr bname(name);
6662 pda.createObject();
6663 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6664 setModified(IsModified_MachineData);
6665 mHWData.backup();
6666 mHWData->mPCIDeviceAssignments.push_back(pda);
6667 }
6668
6669 return S_OK;
6670}
6671
6672/**
6673 * Currently this method doesn't detach device from the running VM,
6674 * just makes sure it's not plugged on next VM start.
6675 */
6676STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6677{
6678 AutoCaller autoCaller(this);
6679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6680
6681 ComObjPtr<PCIDeviceAttachment> pAttach;
6682 bool fRemoved = false;
6683 HRESULT rc;
6684
6685 // lock scope
6686 {
6687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6688
6689 rc = checkStateDependency(MutableStateDep);
6690 if (FAILED(rc)) return rc;
6691
6692 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6693 it != mHWData->mPCIDeviceAssignments.end();
6694 ++it)
6695 {
6696 LONG iHostAddress = -1;
6697 pAttach = *it;
6698 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6699 if (iHostAddress != -1 && iHostAddress == hostAddress)
6700 {
6701 setModified(IsModified_MachineData);
6702 mHWData.backup();
6703 mHWData->mPCIDeviceAssignments.remove(pAttach);
6704 fRemoved = true;
6705 break;
6706 }
6707 }
6708 }
6709
6710
6711 /* Fire event outside of the lock */
6712 if (fRemoved)
6713 {
6714 Assert(!pAttach.isNull());
6715 ComPtr<IEventSource> es;
6716 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6717 Assert(SUCCEEDED(rc));
6718 Bstr mid;
6719 rc = this->COMGETTER(Id)(mid.asOutParam());
6720 Assert(SUCCEEDED(rc));
6721 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6722 }
6723
6724 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6725 tr("No host PCI device %08x attached"),
6726 hostAddress
6727 );
6728}
6729
6730STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6731{
6732 CheckComArgOutSafeArrayPointerValid(aAssignments);
6733
6734 AutoCaller autoCaller(this);
6735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6736
6737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6738
6739 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6740 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6741
6742 return S_OK;
6743}
6744
6745STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6746{
6747 CheckComArgOutPointerValid(aBandwidthControl);
6748
6749 AutoCaller autoCaller(this);
6750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6751
6752 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6753
6754 return S_OK;
6755}
6756
6757STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6758{
6759 CheckComArgOutPointerValid(pfEnabled);
6760 AutoCaller autoCaller(this);
6761 HRESULT hrc = autoCaller.rc();
6762 if (SUCCEEDED(hrc))
6763 {
6764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6765 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6766 }
6767 return hrc;
6768}
6769
6770STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6771{
6772 AutoCaller autoCaller(this);
6773 HRESULT hrc = autoCaller.rc();
6774 if (SUCCEEDED(hrc))
6775 {
6776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6777 hrc = checkStateDependency(MutableStateDep);
6778 if (SUCCEEDED(hrc))
6779 {
6780 hrc = mHWData.backupEx();
6781 if (SUCCEEDED(hrc))
6782 {
6783 setModified(IsModified_MachineData);
6784 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6785 }
6786 }
6787 }
6788 return hrc;
6789}
6790
6791STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6792{
6793 CheckComArgOutPointerValid(pbstrConfig);
6794 AutoCaller autoCaller(this);
6795 HRESULT hrc = autoCaller.rc();
6796 if (SUCCEEDED(hrc))
6797 {
6798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6800 }
6801 return hrc;
6802}
6803
6804STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6805{
6806 CheckComArgStr(bstrConfig);
6807 AutoCaller autoCaller(this);
6808 HRESULT hrc = autoCaller.rc();
6809 if (SUCCEEDED(hrc))
6810 {
6811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6812 hrc = checkStateDependency(MutableStateDep);
6813 if (SUCCEEDED(hrc))
6814 {
6815 hrc = mHWData.backupEx();
6816 if (SUCCEEDED(hrc))
6817 {
6818 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6819 if (SUCCEEDED(hrc))
6820 setModified(IsModified_MachineData);
6821 }
6822 }
6823 }
6824 return hrc;
6825
6826}
6827
6828STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6829{
6830 CheckComArgOutPointerValid(pfAllow);
6831 AutoCaller autoCaller(this);
6832 HRESULT hrc = autoCaller.rc();
6833 if (SUCCEEDED(hrc))
6834 {
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6837 }
6838 return hrc;
6839}
6840
6841STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6842{
6843 AutoCaller autoCaller(this);
6844 HRESULT hrc = autoCaller.rc();
6845 if (SUCCEEDED(hrc))
6846 {
6847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6848 hrc = checkStateDependency(MutableStateDep);
6849 if (SUCCEEDED(hrc))
6850 {
6851 hrc = mHWData.backupEx();
6852 if (SUCCEEDED(hrc))
6853 {
6854 setModified(IsModified_MachineData);
6855 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6856 }
6857 }
6858 }
6859 return hrc;
6860}
6861
6862STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6863{
6864 CheckComArgOutPointerValid(pfEnabled);
6865 AutoCaller autoCaller(this);
6866 HRESULT hrc = autoCaller.rc();
6867 if (SUCCEEDED(hrc))
6868 {
6869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6870 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6871 }
6872 return hrc;
6873}
6874
6875STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6876{
6877 AutoCaller autoCaller(this);
6878 HRESULT hrc = autoCaller.rc();
6879 if (SUCCEEDED(hrc))
6880 {
6881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6882 hrc = checkStateDependency(MutableStateDep);
6883 if ( SUCCEEDED(hrc)
6884 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6885 {
6886 AutostartDb *autostartDb = mParent->getAutostartDb();
6887 int vrc;
6888
6889 if (fEnabled)
6890 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6891 else
6892 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6893
6894 if (RT_SUCCESS(vrc))
6895 {
6896 hrc = mHWData.backupEx();
6897 if (SUCCEEDED(hrc))
6898 {
6899 setModified(IsModified_MachineData);
6900 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6901 }
6902 }
6903 else if (vrc == VERR_NOT_SUPPORTED)
6904 hrc = setError(VBOX_E_NOT_SUPPORTED,
6905 tr("The VM autostart feature is not supported on this platform"));
6906 else if (vrc == VERR_PATH_NOT_FOUND)
6907 hrc = setError(E_FAIL,
6908 tr("The path to the autostart database is not set"));
6909 else
6910 hrc = setError(E_UNEXPECTED,
6911 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6912 fEnabled ? "Adding" : "Removing",
6913 mUserData->s.strName.c_str(), vrc);
6914 }
6915 }
6916 return hrc;
6917}
6918
6919STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6920{
6921 CheckComArgOutPointerValid(puDelay);
6922 AutoCaller autoCaller(this);
6923 HRESULT hrc = autoCaller.rc();
6924 if (SUCCEEDED(hrc))
6925 {
6926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6927 *puDelay = mHWData->mAutostart.uAutostartDelay;
6928 }
6929 return hrc;
6930}
6931
6932STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6933{
6934 AutoCaller autoCaller(this);
6935 HRESULT hrc = autoCaller.rc();
6936 if (SUCCEEDED(hrc))
6937 {
6938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6939 hrc = checkStateDependency(MutableStateDep);
6940 if (SUCCEEDED(hrc))
6941 {
6942 hrc = mHWData.backupEx();
6943 if (SUCCEEDED(hrc))
6944 {
6945 setModified(IsModified_MachineData);
6946 mHWData->mAutostart.uAutostartDelay = uDelay;
6947 }
6948 }
6949 }
6950 return hrc;
6951}
6952
6953STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6954{
6955 CheckComArgOutPointerValid(penmAutostopType);
6956 AutoCaller autoCaller(this);
6957 HRESULT hrc = autoCaller.rc();
6958 if (SUCCEEDED(hrc))
6959 {
6960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6961 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6962 }
6963 return hrc;
6964}
6965
6966STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6967{
6968 AutoCaller autoCaller(this);
6969 HRESULT hrc = autoCaller.rc();
6970 if (SUCCEEDED(hrc))
6971 {
6972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6973 hrc = checkStateDependency(MutableStateDep);
6974 if ( SUCCEEDED(hrc)
6975 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6976 {
6977 AutostartDb *autostartDb = mParent->getAutostartDb();
6978 int vrc;
6979
6980 if (enmAutostopType != AutostopType_Disabled)
6981 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6982 else
6983 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6984
6985 if (RT_SUCCESS(vrc))
6986 {
6987 hrc = mHWData.backupEx();
6988 if (SUCCEEDED(hrc))
6989 {
6990 setModified(IsModified_MachineData);
6991 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6992 }
6993 }
6994 else if (vrc == VERR_NOT_SUPPORTED)
6995 hrc = setError(VBOX_E_NOT_SUPPORTED,
6996 tr("The VM autostop feature is not supported on this platform"));
6997 else if (vrc == VERR_PATH_NOT_FOUND)
6998 hrc = setError(E_FAIL,
6999 tr("The path to the autostart database is not set"));
7000 else
7001 hrc = setError(E_UNEXPECTED,
7002 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7003 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7004 mUserData->s.strName.c_str(), vrc);
7005 }
7006 }
7007 return hrc;
7008}
7009
7010STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7011{
7012 CheckComArgOutPointerValid(aDefaultFrontend);
7013 AutoCaller autoCaller(this);
7014 HRESULT hrc = autoCaller.rc();
7015 if (SUCCEEDED(hrc))
7016 {
7017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7018 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7019 }
7020 return hrc;
7021}
7022
7023STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7024{
7025 CheckComArgStr(aDefaultFrontend);
7026 AutoCaller autoCaller(this);
7027 HRESULT hrc = autoCaller.rc();
7028 if (SUCCEEDED(hrc))
7029 {
7030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7031 hrc = checkStateDependency(MutableOrSavedStateDep);
7032 if (SUCCEEDED(hrc))
7033 {
7034 hrc = mHWData.backupEx();
7035 if (SUCCEEDED(hrc))
7036 {
7037 setModified(IsModified_MachineData);
7038 mHWData->mDefaultFrontend = aDefaultFrontend;
7039 }
7040 }
7041 }
7042 return hrc;
7043}
7044
7045
7046STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7047{
7048 LogFlowFuncEnter();
7049
7050 CheckComArgNotNull(pTarget);
7051 CheckComArgOutPointerValid(pProgress);
7052
7053 /* Convert the options. */
7054 RTCList<CloneOptions_T> optList;
7055 if (options != NULL)
7056 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7057
7058 if (optList.contains(CloneOptions_Link))
7059 {
7060 if (!isSnapshotMachine())
7061 return setError(E_INVALIDARG,
7062 tr("Linked clone can only be created from a snapshot"));
7063 if (mode != CloneMode_MachineState)
7064 return setError(E_INVALIDARG,
7065 tr("Linked clone can only be created for a single machine state"));
7066 }
7067 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7068
7069 AutoCaller autoCaller(this);
7070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7071
7072
7073 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7074
7075 HRESULT rc = pWorker->start(pProgress);
7076
7077 LogFlowFuncLeave();
7078
7079 return rc;
7080}
7081
7082// public methods for internal purposes
7083/////////////////////////////////////////////////////////////////////////////
7084
7085/**
7086 * Adds the given IsModified_* flag to the dirty flags of the machine.
7087 * This must be called either during loadSettings or under the machine write lock.
7088 * @param fl
7089 */
7090void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7091{
7092 mData->flModifications |= fl;
7093 if (fAllowStateModification && isStateModificationAllowed())
7094 mData->mCurrentStateModified = true;
7095}
7096
7097/**
7098 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7099 * care of the write locking.
7100 *
7101 * @param fModifications The flag to add.
7102 */
7103void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7104{
7105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7106 setModified(fModification, fAllowStateModification);
7107}
7108
7109/**
7110 * Saves the registry entry of this machine to the given configuration node.
7111 *
7112 * @param aEntryNode Node to save the registry entry to.
7113 *
7114 * @note locks this object for reading.
7115 */
7116HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7117{
7118 AutoLimitedCaller autoCaller(this);
7119 AssertComRCReturnRC(autoCaller.rc());
7120
7121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7122
7123 data.uuid = mData->mUuid;
7124 data.strSettingsFile = mData->m_strConfigFile;
7125
7126 return S_OK;
7127}
7128
7129/**
7130 * Calculates the absolute path of the given path taking the directory of the
7131 * machine settings file as the current directory.
7132 *
7133 * @param aPath Path to calculate the absolute path for.
7134 * @param aResult Where to put the result (used only on success, can be the
7135 * same Utf8Str instance as passed in @a aPath).
7136 * @return IPRT result.
7137 *
7138 * @note Locks this object for reading.
7139 */
7140int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7141{
7142 AutoCaller autoCaller(this);
7143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7144
7145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7146
7147 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7148
7149 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7150
7151 strSettingsDir.stripFilename();
7152 char folder[RTPATH_MAX];
7153 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7154 if (RT_SUCCESS(vrc))
7155 aResult = folder;
7156
7157 return vrc;
7158}
7159
7160/**
7161 * Copies strSource to strTarget, making it relative to the machine folder
7162 * if it is a subdirectory thereof, or simply copying it otherwise.
7163 *
7164 * @param strSource Path to evaluate and copy.
7165 * @param strTarget Buffer to receive target path.
7166 *
7167 * @note Locks this object for reading.
7168 */
7169void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7170 Utf8Str &strTarget)
7171{
7172 AutoCaller autoCaller(this);
7173 AssertComRCReturn(autoCaller.rc(), (void)0);
7174
7175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7176
7177 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7178 // use strTarget as a temporary buffer to hold the machine settings dir
7179 strTarget = mData->m_strConfigFileFull;
7180 strTarget.stripFilename();
7181 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7182 {
7183 // is relative: then append what's left
7184 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7185 // for empty paths (only possible for subdirs) use "." to avoid
7186 // triggering default settings for not present config attributes.
7187 if (strTarget.isEmpty())
7188 strTarget = ".";
7189 }
7190 else
7191 // is not relative: then overwrite
7192 strTarget = strSource;
7193}
7194
7195/**
7196 * Returns the full path to the machine's log folder in the
7197 * \a aLogFolder argument.
7198 */
7199void Machine::getLogFolder(Utf8Str &aLogFolder)
7200{
7201 AutoCaller autoCaller(this);
7202 AssertComRCReturnVoid(autoCaller.rc());
7203
7204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7205
7206 char szTmp[RTPATH_MAX];
7207 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7208 if (RT_SUCCESS(vrc))
7209 {
7210 if (szTmp[0] && !mUserData.isNull())
7211 {
7212 char szTmp2[RTPATH_MAX];
7213 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7214 if (RT_SUCCESS(vrc))
7215 aLogFolder = BstrFmt("%s%c%s",
7216 szTmp2,
7217 RTPATH_DELIMITER,
7218 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7219 }
7220 else
7221 vrc = VERR_PATH_IS_RELATIVE;
7222 }
7223
7224 if (RT_FAILURE(vrc))
7225 {
7226 // fallback if VBOX_USER_LOGHOME is not set or invalid
7227 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7228 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7229 aLogFolder.append(RTPATH_DELIMITER);
7230 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7231 }
7232}
7233
7234/**
7235 * Returns the full path to the machine's log file for an given index.
7236 */
7237Utf8Str Machine::queryLogFilename(ULONG idx)
7238{
7239 Utf8Str logFolder;
7240 getLogFolder(logFolder);
7241 Assert(logFolder.length());
7242 Utf8Str log;
7243 if (idx == 0)
7244 log = Utf8StrFmt("%s%cVBox.log",
7245 logFolder.c_str(), RTPATH_DELIMITER);
7246 else
7247 log = Utf8StrFmt("%s%cVBox.log.%d",
7248 logFolder.c_str(), RTPATH_DELIMITER, idx);
7249 return log;
7250}
7251
7252/**
7253 * Composes a unique saved state filename based on the current system time. The filename is
7254 * granular to the second so this will work so long as no more than one snapshot is taken on
7255 * a machine per second.
7256 *
7257 * Before version 4.1, we used this formula for saved state files:
7258 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7259 * which no longer works because saved state files can now be shared between the saved state of the
7260 * "saved" machine and an online snapshot, and the following would cause problems:
7261 * 1) save machine
7262 * 2) create online snapshot from that machine state --> reusing saved state file
7263 * 3) save machine again --> filename would be reused, breaking the online snapshot
7264 *
7265 * So instead we now use a timestamp.
7266 *
7267 * @param str
7268 */
7269void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7270{
7271 AutoCaller autoCaller(this);
7272 AssertComRCReturnVoid(autoCaller.rc());
7273
7274 {
7275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7276 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7277 }
7278
7279 RTTIMESPEC ts;
7280 RTTimeNow(&ts);
7281 RTTIME time;
7282 RTTimeExplode(&time, &ts);
7283
7284 strStateFilePath += RTPATH_DELIMITER;
7285 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7286 time.i32Year, time.u8Month, time.u8MonthDay,
7287 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7288}
7289
7290/**
7291 * @note Locks this object for writing, calls the client process
7292 * (inside the lock).
7293 */
7294HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7295 const Utf8Str &strFrontend,
7296 const Utf8Str &strEnvironment,
7297 ProgressProxy *aProgress)
7298{
7299 LogFlowThisFuncEnter();
7300
7301 AssertReturn(aControl, E_FAIL);
7302 AssertReturn(aProgress, E_FAIL);
7303
7304 AutoCaller autoCaller(this);
7305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7306
7307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7308
7309 if (!mData->mRegistered)
7310 return setError(E_UNEXPECTED,
7311 tr("The machine '%s' is not registered"),
7312 mUserData->s.strName.c_str());
7313
7314 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7315
7316 if ( mData->mSession.mState == SessionState_Locked
7317 || mData->mSession.mState == SessionState_Spawning
7318 || mData->mSession.mState == SessionState_Unlocking)
7319 return setError(VBOX_E_INVALID_OBJECT_STATE,
7320 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7321 mUserData->s.strName.c_str());
7322
7323 /* may not be busy */
7324 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7325
7326 /* get the path to the executable */
7327 char szPath[RTPATH_MAX];
7328 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7329 size_t sz = strlen(szPath);
7330 szPath[sz++] = RTPATH_DELIMITER;
7331 szPath[sz] = 0;
7332 char *cmd = szPath + sz;
7333 sz = RTPATH_MAX - sz;
7334
7335 int vrc = VINF_SUCCESS;
7336 RTPROCESS pid = NIL_RTPROCESS;
7337
7338 RTENV env = RTENV_DEFAULT;
7339
7340 if (!strEnvironment.isEmpty())
7341 {
7342 char *newEnvStr = NULL;
7343
7344 do
7345 {
7346 /* clone the current environment */
7347 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7348 AssertRCBreakStmt(vrc2, vrc = vrc2);
7349
7350 newEnvStr = RTStrDup(strEnvironment.c_str());
7351 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7352
7353 /* put new variables to the environment
7354 * (ignore empty variable names here since RTEnv API
7355 * intentionally doesn't do that) */
7356 char *var = newEnvStr;
7357 for (char *p = newEnvStr; *p; ++p)
7358 {
7359 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7360 {
7361 *p = '\0';
7362 if (*var)
7363 {
7364 char *val = strchr(var, '=');
7365 if (val)
7366 {
7367 *val++ = '\0';
7368 vrc2 = RTEnvSetEx(env, var, val);
7369 }
7370 else
7371 vrc2 = RTEnvUnsetEx(env, var);
7372 if (RT_FAILURE(vrc2))
7373 break;
7374 }
7375 var = p + 1;
7376 }
7377 }
7378 if (RT_SUCCESS(vrc2) && *var)
7379 vrc2 = RTEnvPutEx(env, var);
7380
7381 AssertRCBreakStmt(vrc2, vrc = vrc2);
7382 }
7383 while (0);
7384
7385 if (newEnvStr != NULL)
7386 RTStrFree(newEnvStr);
7387 }
7388
7389 /* Qt is default */
7390#ifdef VBOX_WITH_QTGUI
7391 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7392 {
7393# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7394 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7395# else
7396 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7397# endif
7398 Assert(sz >= sizeof(VirtualBox_exe));
7399 strcpy(cmd, VirtualBox_exe);
7400
7401 Utf8Str idStr = mData->mUuid.toString();
7402 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7403 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7404 }
7405#else /* !VBOX_WITH_QTGUI */
7406 if (0)
7407 ;
7408#endif /* VBOX_WITH_QTGUI */
7409
7410 else
7411
7412#ifdef VBOX_WITH_VBOXSDL
7413 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7414 {
7415 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7416 Assert(sz >= sizeof(VBoxSDL_exe));
7417 strcpy(cmd, VBoxSDL_exe);
7418
7419 Utf8Str idStr = mData->mUuid.toString();
7420 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7421 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7422 }
7423#else /* !VBOX_WITH_VBOXSDL */
7424 if (0)
7425 ;
7426#endif /* !VBOX_WITH_VBOXSDL */
7427
7428 else
7429
7430#ifdef VBOX_WITH_HEADLESS
7431 if ( strFrontend == "headless"
7432 || strFrontend == "capture"
7433 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7434 )
7435 {
7436 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7437 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7438 * and a VM works even if the server has not been installed.
7439 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7440 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7441 * differently in 4.0 and 3.x.
7442 */
7443 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7444 Assert(sz >= sizeof(VBoxHeadless_exe));
7445 strcpy(cmd, VBoxHeadless_exe);
7446
7447 Utf8Str idStr = mData->mUuid.toString();
7448 /* Leave space for "--capture" arg. */
7449 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7450 "--startvm", idStr.c_str(),
7451 "--vrde", "config",
7452 0, /* For "--capture". */
7453 0 };
7454 if (strFrontend == "capture")
7455 {
7456 unsigned pos = RT_ELEMENTS(args) - 2;
7457 args[pos] = "--capture";
7458 }
7459 vrc = RTProcCreate(szPath, args, env,
7460#ifdef RT_OS_WINDOWS
7461 RTPROC_FLAGS_NO_WINDOW
7462#else
7463 0
7464#endif
7465 , &pid);
7466 }
7467#else /* !VBOX_WITH_HEADLESS */
7468 if (0)
7469 ;
7470#endif /* !VBOX_WITH_HEADLESS */
7471 else
7472 {
7473 RTEnvDestroy(env);
7474 return setError(E_INVALIDARG,
7475 tr("Invalid frontend name: '%s'"),
7476 strFrontend.c_str());
7477 }
7478
7479 RTEnvDestroy(env);
7480
7481 if (RT_FAILURE(vrc))
7482 return setError(VBOX_E_IPRT_ERROR,
7483 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7484 mUserData->s.strName.c_str(), vrc);
7485
7486 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7487
7488 /*
7489 * Note that we don't release the lock here before calling the client,
7490 * because it doesn't need to call us back if called with a NULL argument.
7491 * Releasing the lock here is dangerous because we didn't prepare the
7492 * launch data yet, but the client we've just started may happen to be
7493 * too fast and call openSession() that will fail (because of PID, etc.),
7494 * so that the Machine will never get out of the Spawning session state.
7495 */
7496
7497 /* inform the session that it will be a remote one */
7498 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7499 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7500 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7501
7502 if (FAILED(rc))
7503 {
7504 /* restore the session state */
7505 mData->mSession.mState = SessionState_Unlocked;
7506 /* The failure may occur w/o any error info (from RPC), so provide one */
7507 return setError(VBOX_E_VM_ERROR,
7508 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7509 }
7510
7511 /* attach launch data to the machine */
7512 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7513 mData->mSession.mRemoteControls.push_back(aControl);
7514 mData->mSession.mProgress = aProgress;
7515 mData->mSession.mPID = pid;
7516 mData->mSession.mState = SessionState_Spawning;
7517 mData->mSession.mType = strFrontend;
7518
7519 LogFlowThisFuncLeave();
7520 return S_OK;
7521}
7522
7523/**
7524 * Returns @c true if the given machine has an open direct session and returns
7525 * the session machine instance and additional session data (on some platforms)
7526 * if so.
7527 *
7528 * Note that when the method returns @c false, the arguments remain unchanged.
7529 *
7530 * @param aMachine Session machine object.
7531 * @param aControl Direct session control object (optional).
7532 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7533 *
7534 * @note locks this object for reading.
7535 */
7536#if defined(RT_OS_WINDOWS)
7537bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7538 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7539 HANDLE *aIPCSem /*= NULL*/,
7540 bool aAllowClosing /*= false*/)
7541#elif defined(RT_OS_OS2)
7542bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7543 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7544 HMTX *aIPCSem /*= NULL*/,
7545 bool aAllowClosing /*= false*/)
7546#else
7547bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7548 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7549 bool aAllowClosing /*= false*/)
7550#endif
7551{
7552 AutoLimitedCaller autoCaller(this);
7553 AssertComRCReturn(autoCaller.rc(), false);
7554
7555 /* just return false for inaccessible machines */
7556 if (autoCaller.state() != Ready)
7557 return false;
7558
7559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7560
7561 if ( mData->mSession.mState == SessionState_Locked
7562 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7563 )
7564 {
7565 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7566
7567 aMachine = mData->mSession.mMachine;
7568
7569 if (aControl != NULL)
7570 *aControl = mData->mSession.mDirectControl;
7571
7572#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7573 /* Additional session data */
7574 if (aIPCSem != NULL)
7575 *aIPCSem = aMachine->mIPCSem;
7576#endif
7577 return true;
7578 }
7579
7580 return false;
7581}
7582
7583/**
7584 * Returns @c true if the given machine has an spawning direct session and
7585 * returns and additional session data (on some platforms) if so.
7586 *
7587 * Note that when the method returns @c false, the arguments remain unchanged.
7588 *
7589 * @param aPID PID of the spawned direct session process.
7590 *
7591 * @note locks this object for reading.
7592 */
7593#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7594bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7595#else
7596bool Machine::isSessionSpawning()
7597#endif
7598{
7599 AutoLimitedCaller autoCaller(this);
7600 AssertComRCReturn(autoCaller.rc(), false);
7601
7602 /* just return false for inaccessible machines */
7603 if (autoCaller.state() != Ready)
7604 return false;
7605
7606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7607
7608 if (mData->mSession.mState == SessionState_Spawning)
7609 {
7610#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7611 /* Additional session data */
7612 if (aPID != NULL)
7613 {
7614 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7615 *aPID = mData->mSession.mPID;
7616 }
7617#endif
7618 return true;
7619 }
7620
7621 return false;
7622}
7623
7624/**
7625 * Called from the client watcher thread to check for unexpected client process
7626 * death during Session_Spawning state (e.g. before it successfully opened a
7627 * direct session).
7628 *
7629 * On Win32 and on OS/2, this method is called only when we've got the
7630 * direct client's process termination notification, so it always returns @c
7631 * true.
7632 *
7633 * On other platforms, this method returns @c true if the client process is
7634 * terminated and @c false if it's still alive.
7635 *
7636 * @note Locks this object for writing.
7637 */
7638bool Machine::checkForSpawnFailure()
7639{
7640 AutoCaller autoCaller(this);
7641 if (!autoCaller.isOk())
7642 {
7643 /* nothing to do */
7644 LogFlowThisFunc(("Already uninitialized!\n"));
7645 return true;
7646 }
7647
7648 /* VirtualBox::addProcessToReap() needs a write lock */
7649 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7650
7651 if (mData->mSession.mState != SessionState_Spawning)
7652 {
7653 /* nothing to do */
7654 LogFlowThisFunc(("Not spawning any more!\n"));
7655 return true;
7656 }
7657
7658 HRESULT rc = S_OK;
7659
7660#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7661
7662 /* the process was already unexpectedly terminated, we just need to set an
7663 * error and finalize session spawning */
7664 rc = setError(E_FAIL,
7665 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7666 getName().c_str());
7667#else
7668
7669 /* PID not yet initialized, skip check. */
7670 if (mData->mSession.mPID == NIL_RTPROCESS)
7671 return false;
7672
7673 RTPROCSTATUS status;
7674 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7675 &status);
7676
7677 if (vrc != VERR_PROCESS_RUNNING)
7678 {
7679 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7680 rc = setError(E_FAIL,
7681 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7682 getName().c_str(), status.iStatus);
7683 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7684 rc = setError(E_FAIL,
7685 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7686 getName().c_str(), status.iStatus);
7687 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7688 rc = setError(E_FAIL,
7689 tr("The virtual machine '%s' has terminated abnormally"),
7690 getName().c_str(), status.iStatus);
7691 else
7692 rc = setError(E_FAIL,
7693 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7694 getName().c_str(), rc);
7695 }
7696
7697#endif
7698
7699 if (FAILED(rc))
7700 {
7701 /* Close the remote session, remove the remote control from the list
7702 * and reset session state to Closed (@note keep the code in sync with
7703 * the relevant part in checkForSpawnFailure()). */
7704
7705 Assert(mData->mSession.mRemoteControls.size() == 1);
7706 if (mData->mSession.mRemoteControls.size() == 1)
7707 {
7708 ErrorInfoKeeper eik;
7709 mData->mSession.mRemoteControls.front()->Uninitialize();
7710 }
7711
7712 mData->mSession.mRemoteControls.clear();
7713 mData->mSession.mState = SessionState_Unlocked;
7714
7715 /* finalize the progress after setting the state */
7716 if (!mData->mSession.mProgress.isNull())
7717 {
7718 mData->mSession.mProgress->notifyComplete(rc);
7719 mData->mSession.mProgress.setNull();
7720 }
7721
7722 mParent->addProcessToReap(mData->mSession.mPID);
7723 mData->mSession.mPID = NIL_RTPROCESS;
7724
7725 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7726 return true;
7727 }
7728
7729 return false;
7730}
7731
7732/**
7733 * Checks whether the machine can be registered. If so, commits and saves
7734 * all settings.
7735 *
7736 * @note Must be called from mParent's write lock. Locks this object and
7737 * children for writing.
7738 */
7739HRESULT Machine::prepareRegister()
7740{
7741 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7742
7743 AutoLimitedCaller autoCaller(this);
7744 AssertComRCReturnRC(autoCaller.rc());
7745
7746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7747
7748 /* wait for state dependents to drop to zero */
7749 ensureNoStateDependencies();
7750
7751 if (!mData->mAccessible)
7752 return setError(VBOX_E_INVALID_OBJECT_STATE,
7753 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7754 mUserData->s.strName.c_str(),
7755 mData->mUuid.toString().c_str());
7756
7757 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7758
7759 if (mData->mRegistered)
7760 return setError(VBOX_E_INVALID_OBJECT_STATE,
7761 tr("The machine '%s' with UUID {%s} is already registered"),
7762 mUserData->s.strName.c_str(),
7763 mData->mUuid.toString().c_str());
7764
7765 HRESULT rc = S_OK;
7766
7767 // Ensure the settings are saved. If we are going to be registered and
7768 // no config file exists yet, create it by calling saveSettings() too.
7769 if ( (mData->flModifications)
7770 || (!mData->pMachineConfigFile->fileExists())
7771 )
7772 {
7773 rc = saveSettings(NULL);
7774 // no need to check whether VirtualBox.xml needs saving too since
7775 // we can't have a machine XML file rename pending
7776 if (FAILED(rc)) return rc;
7777 }
7778
7779 /* more config checking goes here */
7780
7781 if (SUCCEEDED(rc))
7782 {
7783 /* we may have had implicit modifications we want to fix on success */
7784 commit();
7785
7786 mData->mRegistered = true;
7787 }
7788 else
7789 {
7790 /* we may have had implicit modifications we want to cancel on failure*/
7791 rollback(false /* aNotify */);
7792 }
7793
7794 return rc;
7795}
7796
7797/**
7798 * Increases the number of objects dependent on the machine state or on the
7799 * registered state. Guarantees that these two states will not change at least
7800 * until #releaseStateDependency() is called.
7801 *
7802 * Depending on the @a aDepType value, additional state checks may be made.
7803 * These checks will set extended error info on failure. See
7804 * #checkStateDependency() for more info.
7805 *
7806 * If this method returns a failure, the dependency is not added and the caller
7807 * is not allowed to rely on any particular machine state or registration state
7808 * value and may return the failed result code to the upper level.
7809 *
7810 * @param aDepType Dependency type to add.
7811 * @param aState Current machine state (NULL if not interested).
7812 * @param aRegistered Current registered state (NULL if not interested).
7813 *
7814 * @note Locks this object for writing.
7815 */
7816HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7817 MachineState_T *aState /* = NULL */,
7818 BOOL *aRegistered /* = NULL */)
7819{
7820 AutoCaller autoCaller(this);
7821 AssertComRCReturnRC(autoCaller.rc());
7822
7823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7824
7825 HRESULT rc = checkStateDependency(aDepType);
7826 if (FAILED(rc)) return rc;
7827
7828 {
7829 if (mData->mMachineStateChangePending != 0)
7830 {
7831 /* ensureNoStateDependencies() is waiting for state dependencies to
7832 * drop to zero so don't add more. It may make sense to wait a bit
7833 * and retry before reporting an error (since the pending state
7834 * transition should be really quick) but let's just assert for
7835 * now to see if it ever happens on practice. */
7836
7837 AssertFailed();
7838
7839 return setError(E_ACCESSDENIED,
7840 tr("Machine state change is in progress. Please retry the operation later."));
7841 }
7842
7843 ++mData->mMachineStateDeps;
7844 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7845 }
7846
7847 if (aState)
7848 *aState = mData->mMachineState;
7849 if (aRegistered)
7850 *aRegistered = mData->mRegistered;
7851
7852 return S_OK;
7853}
7854
7855/**
7856 * Decreases the number of objects dependent on the machine state.
7857 * Must always complete the #addStateDependency() call after the state
7858 * dependency is no more necessary.
7859 */
7860void Machine::releaseStateDependency()
7861{
7862 AutoCaller autoCaller(this);
7863 AssertComRCReturnVoid(autoCaller.rc());
7864
7865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7866
7867 /* releaseStateDependency() w/o addStateDependency()? */
7868 AssertReturnVoid(mData->mMachineStateDeps != 0);
7869 -- mData->mMachineStateDeps;
7870
7871 if (mData->mMachineStateDeps == 0)
7872 {
7873 /* inform ensureNoStateDependencies() that there are no more deps */
7874 if (mData->mMachineStateChangePending != 0)
7875 {
7876 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7877 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7878 }
7879 }
7880}
7881
7882Utf8Str Machine::getExtraData(const Utf8Str &strKey)
7883{
7884 /* start with nothing found */
7885 Utf8Str strResult("");
7886
7887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7888
7889 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7890 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7891 // found:
7892 strResult = it->second; // source is a Utf8Str
7893
7894 return strResult;
7895}
7896
7897// protected methods
7898/////////////////////////////////////////////////////////////////////////////
7899
7900/**
7901 * Performs machine state checks based on the @a aDepType value. If a check
7902 * fails, this method will set extended error info, otherwise it will return
7903 * S_OK. It is supposed, that on failure, the caller will immediately return
7904 * the return value of this method to the upper level.
7905 *
7906 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7907 *
7908 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7909 * current state of this machine object allows to change settings of the
7910 * machine (i.e. the machine is not registered, or registered but not running
7911 * and not saved). It is useful to call this method from Machine setters
7912 * before performing any change.
7913 *
7914 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7915 * as for MutableStateDep except that if the machine is saved, S_OK is also
7916 * returned. This is useful in setters which allow changing machine
7917 * properties when it is in the saved state.
7918 *
7919 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7920 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7921 * Aborted).
7922 *
7923 * @param aDepType Dependency type to check.
7924 *
7925 * @note Non Machine based classes should use #addStateDependency() and
7926 * #releaseStateDependency() methods or the smart AutoStateDependency
7927 * template.
7928 *
7929 * @note This method must be called from under this object's read or write
7930 * lock.
7931 */
7932HRESULT Machine::checkStateDependency(StateDependency aDepType)
7933{
7934 switch (aDepType)
7935 {
7936 case AnyStateDep:
7937 {
7938 break;
7939 }
7940 case MutableStateDep:
7941 {
7942 if ( mData->mRegistered
7943 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7944 || ( mData->mMachineState != MachineState_Paused
7945 && mData->mMachineState != MachineState_Running
7946 && mData->mMachineState != MachineState_Aborted
7947 && mData->mMachineState != MachineState_Teleported
7948 && mData->mMachineState != MachineState_PoweredOff
7949 )
7950 )
7951 )
7952 return setError(VBOX_E_INVALID_VM_STATE,
7953 tr("The machine is not mutable (state is %s)"),
7954 Global::stringifyMachineState(mData->mMachineState));
7955 break;
7956 }
7957 case MutableOrSavedStateDep:
7958 {
7959 if ( mData->mRegistered
7960 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7961 || ( mData->mMachineState != MachineState_Paused
7962 && mData->mMachineState != MachineState_Running
7963 && mData->mMachineState != MachineState_Aborted
7964 && mData->mMachineState != MachineState_Teleported
7965 && mData->mMachineState != MachineState_Saved
7966 && mData->mMachineState != MachineState_PoweredOff
7967 )
7968 )
7969 )
7970 return setError(VBOX_E_INVALID_VM_STATE,
7971 tr("The machine is not mutable (state is %s)"),
7972 Global::stringifyMachineState(mData->mMachineState));
7973 break;
7974 }
7975 case OfflineStateDep:
7976 {
7977 if ( mData->mRegistered
7978 && ( !isSessionMachine()
7979 || ( mData->mMachineState != MachineState_PoweredOff
7980 && mData->mMachineState != MachineState_Saved
7981 && mData->mMachineState != MachineState_Aborted
7982 && mData->mMachineState != MachineState_Teleported
7983 )
7984 )
7985 )
7986 return setError(VBOX_E_INVALID_VM_STATE,
7987 tr("The machine is not offline (state is %s)"),
7988 Global::stringifyMachineState(mData->mMachineState));
7989 break;
7990 }
7991 }
7992
7993 return S_OK;
7994}
7995
7996/**
7997 * Helper to initialize all associated child objects and allocate data
7998 * structures.
7999 *
8000 * This method must be called as a part of the object's initialization procedure
8001 * (usually done in the #init() method).
8002 *
8003 * @note Must be called only from #init() or from #registeredInit().
8004 */
8005HRESULT Machine::initDataAndChildObjects()
8006{
8007 AutoCaller autoCaller(this);
8008 AssertComRCReturnRC(autoCaller.rc());
8009 AssertComRCReturn(autoCaller.state() == InInit ||
8010 autoCaller.state() == Limited, E_FAIL);
8011
8012 AssertReturn(!mData->mAccessible, E_FAIL);
8013
8014 /* allocate data structures */
8015 mSSData.allocate();
8016 mUserData.allocate();
8017 mHWData.allocate();
8018 mMediaData.allocate();
8019 mStorageControllers.allocate();
8020
8021 /* initialize mOSTypeId */
8022 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8023
8024 /* create associated BIOS settings object */
8025 unconst(mBIOSSettings).createObject();
8026 mBIOSSettings->init(this);
8027
8028 /* create an associated VRDE object (default is disabled) */
8029 unconst(mVRDEServer).createObject();
8030 mVRDEServer->init(this);
8031
8032 /* create associated serial port objects */
8033 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8034 {
8035 unconst(mSerialPorts[slot]).createObject();
8036 mSerialPorts[slot]->init(this, slot);
8037 }
8038
8039 /* create associated parallel port objects */
8040 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8041 {
8042 unconst(mParallelPorts[slot]).createObject();
8043 mParallelPorts[slot]->init(this, slot);
8044 }
8045
8046 /* create the audio adapter object (always present, default is disabled) */
8047 unconst(mAudioAdapter).createObject();
8048 mAudioAdapter->init(this);
8049
8050 /* create the USB controller object (always present, default is disabled) */
8051 unconst(mUSBController).createObject();
8052 mUSBController->init(this);
8053
8054 /* create associated network adapter objects */
8055 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8056 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8057 {
8058 unconst(mNetworkAdapters[slot]).createObject();
8059 mNetworkAdapters[slot]->init(this, slot);
8060 }
8061
8062 /* create the bandwidth control */
8063 unconst(mBandwidthControl).createObject();
8064 mBandwidthControl->init(this);
8065
8066 return S_OK;
8067}
8068
8069/**
8070 * Helper to uninitialize all associated child objects and to free all data
8071 * structures.
8072 *
8073 * This method must be called as a part of the object's uninitialization
8074 * procedure (usually done in the #uninit() method).
8075 *
8076 * @note Must be called only from #uninit() or from #registeredInit().
8077 */
8078void Machine::uninitDataAndChildObjects()
8079{
8080 AutoCaller autoCaller(this);
8081 AssertComRCReturnVoid(autoCaller.rc());
8082 AssertComRCReturnVoid( autoCaller.state() == InUninit
8083 || autoCaller.state() == Limited);
8084
8085 /* tell all our other child objects we've been uninitialized */
8086 if (mBandwidthControl)
8087 {
8088 mBandwidthControl->uninit();
8089 unconst(mBandwidthControl).setNull();
8090 }
8091
8092 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8093 {
8094 if (mNetworkAdapters[slot])
8095 {
8096 mNetworkAdapters[slot]->uninit();
8097 unconst(mNetworkAdapters[slot]).setNull();
8098 }
8099 }
8100
8101 if (mUSBController)
8102 {
8103 mUSBController->uninit();
8104 unconst(mUSBController).setNull();
8105 }
8106
8107 if (mAudioAdapter)
8108 {
8109 mAudioAdapter->uninit();
8110 unconst(mAudioAdapter).setNull();
8111 }
8112
8113 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8114 {
8115 if (mParallelPorts[slot])
8116 {
8117 mParallelPorts[slot]->uninit();
8118 unconst(mParallelPorts[slot]).setNull();
8119 }
8120 }
8121
8122 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8123 {
8124 if (mSerialPorts[slot])
8125 {
8126 mSerialPorts[slot]->uninit();
8127 unconst(mSerialPorts[slot]).setNull();
8128 }
8129 }
8130
8131 if (mVRDEServer)
8132 {
8133 mVRDEServer->uninit();
8134 unconst(mVRDEServer).setNull();
8135 }
8136
8137 if (mBIOSSettings)
8138 {
8139 mBIOSSettings->uninit();
8140 unconst(mBIOSSettings).setNull();
8141 }
8142
8143 /* Deassociate media (only when a real Machine or a SnapshotMachine
8144 * instance is uninitialized; SessionMachine instances refer to real
8145 * Machine media). This is necessary for a clean re-initialization of
8146 * the VM after successfully re-checking the accessibility state. Note
8147 * that in case of normal Machine or SnapshotMachine uninitialization (as
8148 * a result of unregistering or deleting the snapshot), outdated media
8149 * attachments will already be uninitialized and deleted, so this
8150 * code will not affect them. */
8151 if ( !!mMediaData
8152 && (!isSessionMachine())
8153 )
8154 {
8155 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8156 it != mMediaData->mAttachments.end();
8157 ++it)
8158 {
8159 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8160 if (pMedium.isNull())
8161 continue;
8162 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8163 AssertComRC(rc);
8164 }
8165 }
8166
8167 if (!isSessionMachine() && !isSnapshotMachine())
8168 {
8169 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8170 if (mData->mFirstSnapshot)
8171 {
8172 // snapshots tree is protected by machine write lock; strictly
8173 // this isn't necessary here since we're deleting the entire
8174 // machine, but otherwise we assert in Snapshot::uninit()
8175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8176 mData->mFirstSnapshot->uninit();
8177 mData->mFirstSnapshot.setNull();
8178 }
8179
8180 mData->mCurrentSnapshot.setNull();
8181 }
8182
8183 /* free data structures (the essential mData structure is not freed here
8184 * since it may be still in use) */
8185 mMediaData.free();
8186 mStorageControllers.free();
8187 mHWData.free();
8188 mUserData.free();
8189 mSSData.free();
8190}
8191
8192/**
8193 * Returns a pointer to the Machine object for this machine that acts like a
8194 * parent for complex machine data objects such as shared folders, etc.
8195 *
8196 * For primary Machine objects and for SnapshotMachine objects, returns this
8197 * object's pointer itself. For SessionMachine objects, returns the peer
8198 * (primary) machine pointer.
8199 */
8200Machine* Machine::getMachine()
8201{
8202 if (isSessionMachine())
8203 return (Machine*)mPeer;
8204 return this;
8205}
8206
8207/**
8208 * Makes sure that there are no machine state dependents. If necessary, waits
8209 * for the number of dependents to drop to zero.
8210 *
8211 * Make sure this method is called from under this object's write lock to
8212 * guarantee that no new dependents may be added when this method returns
8213 * control to the caller.
8214 *
8215 * @note Locks this object for writing. The lock will be released while waiting
8216 * (if necessary).
8217 *
8218 * @warning To be used only in methods that change the machine state!
8219 */
8220void Machine::ensureNoStateDependencies()
8221{
8222 AssertReturnVoid(isWriteLockOnCurrentThread());
8223
8224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8225
8226 /* Wait for all state dependents if necessary */
8227 if (mData->mMachineStateDeps != 0)
8228 {
8229 /* lazy semaphore creation */
8230 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8231 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8232
8233 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8234 mData->mMachineStateDeps));
8235
8236 ++mData->mMachineStateChangePending;
8237
8238 /* reset the semaphore before waiting, the last dependent will signal
8239 * it */
8240 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8241
8242 alock.release();
8243
8244 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8245
8246 alock.acquire();
8247
8248 -- mData->mMachineStateChangePending;
8249 }
8250}
8251
8252/**
8253 * Changes the machine state and informs callbacks.
8254 *
8255 * This method is not intended to fail so it either returns S_OK or asserts (and
8256 * returns a failure).
8257 *
8258 * @note Locks this object for writing.
8259 */
8260HRESULT Machine::setMachineState(MachineState_T aMachineState)
8261{
8262 LogFlowThisFuncEnter();
8263 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8264
8265 AutoCaller autoCaller(this);
8266 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8267
8268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8269
8270 /* wait for state dependents to drop to zero */
8271 ensureNoStateDependencies();
8272
8273 if (mData->mMachineState != aMachineState)
8274 {
8275 mData->mMachineState = aMachineState;
8276
8277 RTTimeNow(&mData->mLastStateChange);
8278
8279 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8280 }
8281
8282 LogFlowThisFuncLeave();
8283 return S_OK;
8284}
8285
8286/**
8287 * Searches for a shared folder with the given logical name
8288 * in the collection of shared folders.
8289 *
8290 * @param aName logical name of the shared folder
8291 * @param aSharedFolder where to return the found object
8292 * @param aSetError whether to set the error info if the folder is
8293 * not found
8294 * @return
8295 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8296 *
8297 * @note
8298 * must be called from under the object's lock!
8299 */
8300HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8301 ComObjPtr<SharedFolder> &aSharedFolder,
8302 bool aSetError /* = false */)
8303{
8304 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8305 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8306 it != mHWData->mSharedFolders.end();
8307 ++it)
8308 {
8309 SharedFolder *pSF = *it;
8310 AutoCaller autoCaller(pSF);
8311 if (pSF->getName() == aName)
8312 {
8313 aSharedFolder = pSF;
8314 rc = S_OK;
8315 break;
8316 }
8317 }
8318
8319 if (aSetError && FAILED(rc))
8320 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8321
8322 return rc;
8323}
8324
8325/**
8326 * Initializes all machine instance data from the given settings structures
8327 * from XML. The exception is the machine UUID which needs special handling
8328 * depending on the caller's use case, so the caller needs to set that herself.
8329 *
8330 * This gets called in several contexts during machine initialization:
8331 *
8332 * -- When machine XML exists on disk already and needs to be loaded into memory,
8333 * for example, from registeredInit() to load all registered machines on
8334 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8335 * attached to the machine should be part of some media registry already.
8336 *
8337 * -- During OVF import, when a machine config has been constructed from an
8338 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8339 * ensure that the media listed as attachments in the config (which have
8340 * been imported from the OVF) receive the correct registry ID.
8341 *
8342 * -- During VM cloning.
8343 *
8344 * @param config Machine settings from XML.
8345 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8346 * @return
8347 */
8348HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8349 const Guid *puuidRegistry)
8350{
8351 // copy name, description, OS type, teleporter, UTC etc.
8352 mUserData->s = config.machineUserData;
8353
8354 // look up the object by Id to check it is valid
8355 ComPtr<IGuestOSType> guestOSType;
8356 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8357 guestOSType.asOutParam());
8358 if (FAILED(rc)) return rc;
8359
8360 // stateFile (optional)
8361 if (config.strStateFile.isEmpty())
8362 mSSData->strStateFilePath.setNull();
8363 else
8364 {
8365 Utf8Str stateFilePathFull(config.strStateFile);
8366 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8367 if (RT_FAILURE(vrc))
8368 return setError(E_FAIL,
8369 tr("Invalid saved state file path '%s' (%Rrc)"),
8370 config.strStateFile.c_str(),
8371 vrc);
8372 mSSData->strStateFilePath = stateFilePathFull;
8373 }
8374
8375 // snapshot folder needs special processing so set it again
8376 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8377 if (FAILED(rc)) return rc;
8378
8379 /* Copy the extra data items (Not in any case config is already the same as
8380 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8381 * make sure the extra data map is copied). */
8382 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8383
8384 /* currentStateModified (optional, default is true) */
8385 mData->mCurrentStateModified = config.fCurrentStateModified;
8386
8387 mData->mLastStateChange = config.timeLastStateChange;
8388
8389 /*
8390 * note: all mUserData members must be assigned prior this point because
8391 * we need to commit changes in order to let mUserData be shared by all
8392 * snapshot machine instances.
8393 */
8394 mUserData.commitCopy();
8395
8396 // machine registry, if present (must be loaded before snapshots)
8397 if (config.canHaveOwnMediaRegistry())
8398 {
8399 // determine machine folder
8400 Utf8Str strMachineFolder = getSettingsFileFull();
8401 strMachineFolder.stripFilename();
8402 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8403 config.mediaRegistry,
8404 strMachineFolder);
8405 if (FAILED(rc)) return rc;
8406 }
8407
8408 /* Snapshot node (optional) */
8409 size_t cRootSnapshots;
8410 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8411 {
8412 // there must be only one root snapshot
8413 Assert(cRootSnapshots == 1);
8414
8415 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8416
8417 rc = loadSnapshot(snap,
8418 config.uuidCurrentSnapshot,
8419 NULL); // no parent == first snapshot
8420 if (FAILED(rc)) return rc;
8421 }
8422
8423 // hardware data
8424 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8425 if (FAILED(rc)) return rc;
8426
8427 // load storage controllers
8428 rc = loadStorageControllers(config.storageMachine,
8429 puuidRegistry,
8430 NULL /* puuidSnapshot */);
8431 if (FAILED(rc)) return rc;
8432
8433 /*
8434 * NOTE: the assignment below must be the last thing to do,
8435 * otherwise it will be not possible to change the settings
8436 * somewhere in the code above because all setters will be
8437 * blocked by checkStateDependency(MutableStateDep).
8438 */
8439
8440 /* set the machine state to Aborted or Saved when appropriate */
8441 if (config.fAborted)
8442 {
8443 mSSData->strStateFilePath.setNull();
8444
8445 /* no need to use setMachineState() during init() */
8446 mData->mMachineState = MachineState_Aborted;
8447 }
8448 else if (!mSSData->strStateFilePath.isEmpty())
8449 {
8450 /* no need to use setMachineState() during init() */
8451 mData->mMachineState = MachineState_Saved;
8452 }
8453
8454 // after loading settings, we are no longer different from the XML on disk
8455 mData->flModifications = 0;
8456
8457 return S_OK;
8458}
8459
8460/**
8461 * Recursively loads all snapshots starting from the given.
8462 *
8463 * @param aNode <Snapshot> node.
8464 * @param aCurSnapshotId Current snapshot ID from the settings file.
8465 * @param aParentSnapshot Parent snapshot.
8466 */
8467HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8468 const Guid &aCurSnapshotId,
8469 Snapshot *aParentSnapshot)
8470{
8471 AssertReturn(!isSnapshotMachine(), E_FAIL);
8472 AssertReturn(!isSessionMachine(), E_FAIL);
8473
8474 HRESULT rc = S_OK;
8475
8476 Utf8Str strStateFile;
8477 if (!data.strStateFile.isEmpty())
8478 {
8479 /* optional */
8480 strStateFile = data.strStateFile;
8481 int vrc = calculateFullPath(strStateFile, strStateFile);
8482 if (RT_FAILURE(vrc))
8483 return setError(E_FAIL,
8484 tr("Invalid saved state file path '%s' (%Rrc)"),
8485 strStateFile.c_str(),
8486 vrc);
8487 }
8488
8489 /* create a snapshot machine object */
8490 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8491 pSnapshotMachine.createObject();
8492 rc = pSnapshotMachine->initFromSettings(this,
8493 data.hardware,
8494 &data.debugging,
8495 &data.autostart,
8496 data.storage,
8497 data.uuid.ref(),
8498 strStateFile);
8499 if (FAILED(rc)) return rc;
8500
8501 /* create a snapshot object */
8502 ComObjPtr<Snapshot> pSnapshot;
8503 pSnapshot.createObject();
8504 /* initialize the snapshot */
8505 rc = pSnapshot->init(mParent, // VirtualBox object
8506 data.uuid,
8507 data.strName,
8508 data.strDescription,
8509 data.timestamp,
8510 pSnapshotMachine,
8511 aParentSnapshot);
8512 if (FAILED(rc)) return rc;
8513
8514 /* memorize the first snapshot if necessary */
8515 if (!mData->mFirstSnapshot)
8516 mData->mFirstSnapshot = pSnapshot;
8517
8518 /* memorize the current snapshot when appropriate */
8519 if ( !mData->mCurrentSnapshot
8520 && pSnapshot->getId() == aCurSnapshotId
8521 )
8522 mData->mCurrentSnapshot = pSnapshot;
8523
8524 // now create the children
8525 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8526 it != data.llChildSnapshots.end();
8527 ++it)
8528 {
8529 const settings::Snapshot &childData = *it;
8530 // recurse
8531 rc = loadSnapshot(childData,
8532 aCurSnapshotId,
8533 pSnapshot); // parent = the one we created above
8534 if (FAILED(rc)) return rc;
8535 }
8536
8537 return rc;
8538}
8539
8540/**
8541 * Loads settings into mHWData.
8542 *
8543 * @param data Reference to the hardware settings.
8544 * @param pDbg Pointer to the debugging settings.
8545 * @param pAutostart Pointer to the autostart settings.
8546 */
8547HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8548 const settings::Autostart *pAutostart)
8549{
8550 AssertReturn(!isSessionMachine(), E_FAIL);
8551
8552 HRESULT rc = S_OK;
8553
8554 try
8555 {
8556 /* The hardware version attribute (optional). */
8557 mHWData->mHWVersion = data.strVersion;
8558 mHWData->mHardwareUUID = data.uuid;
8559
8560 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8561 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8562 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8563 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8564 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8565 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8566 mHWData->mPAEEnabled = data.fPAE;
8567 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8568
8569 mHWData->mCPUCount = data.cCPUs;
8570 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8571 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8572
8573 // cpu
8574 if (mHWData->mCPUHotPlugEnabled)
8575 {
8576 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8577 it != data.llCpus.end();
8578 ++it)
8579 {
8580 const settings::Cpu &cpu = *it;
8581
8582 mHWData->mCPUAttached[cpu.ulId] = true;
8583 }
8584 }
8585
8586 // cpuid leafs
8587 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8588 it != data.llCpuIdLeafs.end();
8589 ++it)
8590 {
8591 const settings::CpuIdLeaf &leaf = *it;
8592
8593 switch (leaf.ulId)
8594 {
8595 case 0x0:
8596 case 0x1:
8597 case 0x2:
8598 case 0x3:
8599 case 0x4:
8600 case 0x5:
8601 case 0x6:
8602 case 0x7:
8603 case 0x8:
8604 case 0x9:
8605 case 0xA:
8606 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8607 break;
8608
8609 case 0x80000000:
8610 case 0x80000001:
8611 case 0x80000002:
8612 case 0x80000003:
8613 case 0x80000004:
8614 case 0x80000005:
8615 case 0x80000006:
8616 case 0x80000007:
8617 case 0x80000008:
8618 case 0x80000009:
8619 case 0x8000000A:
8620 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8621 break;
8622
8623 default:
8624 /* just ignore */
8625 break;
8626 }
8627 }
8628
8629 mHWData->mMemorySize = data.ulMemorySizeMB;
8630 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8631
8632 // boot order
8633 for (size_t i = 0;
8634 i < RT_ELEMENTS(mHWData->mBootOrder);
8635 i++)
8636 {
8637 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8638 if (it == data.mapBootOrder.end())
8639 mHWData->mBootOrder[i] = DeviceType_Null;
8640 else
8641 mHWData->mBootOrder[i] = it->second;
8642 }
8643
8644 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8645 mHWData->mMonitorCount = data.cMonitors;
8646 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8647 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8648 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8649 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8650 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8651 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8652 mHWData->mFirmwareType = data.firmwareType;
8653 mHWData->mPointingHIDType = data.pointingHIDType;
8654 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8655 mHWData->mChipsetType = data.chipsetType;
8656 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8657 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8658 mHWData->mHPETEnabled = data.fHPETEnabled;
8659
8660 /* VRDEServer */
8661 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8662 if (FAILED(rc)) return rc;
8663
8664 /* BIOS */
8665 rc = mBIOSSettings->loadSettings(data.biosSettings);
8666 if (FAILED(rc)) return rc;
8667
8668 // Bandwidth control (must come before network adapters)
8669 rc = mBandwidthControl->loadSettings(data.ioSettings);
8670 if (FAILED(rc)) return rc;
8671
8672 /* USB Controller */
8673 rc = mUSBController->loadSettings(data.usbController);
8674 if (FAILED(rc)) return rc;
8675
8676 // network adapters
8677 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8678 uint32_t oldCount = mNetworkAdapters.size();
8679 if (newCount > oldCount)
8680 {
8681 mNetworkAdapters.resize(newCount);
8682 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8683 {
8684 unconst(mNetworkAdapters[slot]).createObject();
8685 mNetworkAdapters[slot]->init(this, slot);
8686 }
8687 }
8688 else if (newCount < oldCount)
8689 mNetworkAdapters.resize(newCount);
8690 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8691 it != data.llNetworkAdapters.end();
8692 ++it)
8693 {
8694 const settings::NetworkAdapter &nic = *it;
8695
8696 /* slot unicity is guaranteed by XML Schema */
8697 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8698 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8699 if (FAILED(rc)) return rc;
8700 }
8701
8702 // serial ports
8703 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8704 it != data.llSerialPorts.end();
8705 ++it)
8706 {
8707 const settings::SerialPort &s = *it;
8708
8709 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8710 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8711 if (FAILED(rc)) return rc;
8712 }
8713
8714 // parallel ports (optional)
8715 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8716 it != data.llParallelPorts.end();
8717 ++it)
8718 {
8719 const settings::ParallelPort &p = *it;
8720
8721 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8722 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8723 if (FAILED(rc)) return rc;
8724 }
8725
8726 /* AudioAdapter */
8727 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8728 if (FAILED(rc)) return rc;
8729
8730 /* Shared folders */
8731 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8732 it != data.llSharedFolders.end();
8733 ++it)
8734 {
8735 const settings::SharedFolder &sf = *it;
8736
8737 ComObjPtr<SharedFolder> sharedFolder;
8738 /* Check for double entries. Not allowed! */
8739 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8740 if (SUCCEEDED(rc))
8741 return setError(VBOX_E_OBJECT_IN_USE,
8742 tr("Shared folder named '%s' already exists"),
8743 sf.strName.c_str());
8744
8745 /* Create the new shared folder. Don't break on error. This will be
8746 * reported when the machine starts. */
8747 sharedFolder.createObject();
8748 rc = sharedFolder->init(getMachine(),
8749 sf.strName,
8750 sf.strHostPath,
8751 RT_BOOL(sf.fWritable),
8752 RT_BOOL(sf.fAutoMount),
8753 false /* fFailOnError */);
8754 if (FAILED(rc)) return rc;
8755 mHWData->mSharedFolders.push_back(sharedFolder);
8756 }
8757
8758 // Clipboard
8759 mHWData->mClipboardMode = data.clipboardMode;
8760
8761 // drag'n'drop
8762 mHWData->mDragAndDropMode = data.dragAndDropMode;
8763
8764 // guest settings
8765 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8766
8767 // IO settings
8768 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8769 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8770
8771 // Host PCI devices
8772 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8773 it != data.pciAttachments.end();
8774 ++it)
8775 {
8776 const settings::HostPCIDeviceAttachment &hpda = *it;
8777 ComObjPtr<PCIDeviceAttachment> pda;
8778
8779 pda.createObject();
8780 pda->loadSettings(this, hpda);
8781 mHWData->mPCIDeviceAssignments.push_back(pda);
8782 }
8783
8784 /*
8785 * (The following isn't really real hardware, but it lives in HWData
8786 * for reasons of convenience.)
8787 */
8788
8789#ifdef VBOX_WITH_GUEST_PROPS
8790 /* Guest properties (optional) */
8791 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8792 it != data.llGuestProperties.end();
8793 ++it)
8794 {
8795 const settings::GuestProperty &prop = *it;
8796 uint32_t fFlags = guestProp::NILFLAG;
8797 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8798 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8799 mHWData->mGuestProperties[prop.strName] = property;
8800 }
8801
8802 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8803#endif /* VBOX_WITH_GUEST_PROPS defined */
8804
8805 rc = loadDebugging(pDbg);
8806 if (FAILED(rc))
8807 return rc;
8808
8809 mHWData->mAutostart = *pAutostart;
8810
8811 /* default frontend */
8812 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8813 }
8814 catch(std::bad_alloc &)
8815 {
8816 return E_OUTOFMEMORY;
8817 }
8818
8819 AssertComRC(rc);
8820 return rc;
8821}
8822
8823/**
8824 * Called from Machine::loadHardware() to load the debugging settings of the
8825 * machine.
8826 *
8827 * @param pDbg Pointer to the settings.
8828 */
8829HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8830{
8831 mHWData->mDebugging = *pDbg;
8832 /* no more processing currently required, this will probably change. */
8833 return S_OK;
8834}
8835
8836/**
8837 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8838 *
8839 * @param data
8840 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8841 * @param puuidSnapshot
8842 * @return
8843 */
8844HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8845 const Guid *puuidRegistry,
8846 const Guid *puuidSnapshot)
8847{
8848 AssertReturn(!isSessionMachine(), E_FAIL);
8849
8850 HRESULT rc = S_OK;
8851
8852 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8853 it != data.llStorageControllers.end();
8854 ++it)
8855 {
8856 const settings::StorageController &ctlData = *it;
8857
8858 ComObjPtr<StorageController> pCtl;
8859 /* Try to find one with the name first. */
8860 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8861 if (SUCCEEDED(rc))
8862 return setError(VBOX_E_OBJECT_IN_USE,
8863 tr("Storage controller named '%s' already exists"),
8864 ctlData.strName.c_str());
8865
8866 pCtl.createObject();
8867 rc = pCtl->init(this,
8868 ctlData.strName,
8869 ctlData.storageBus,
8870 ctlData.ulInstance,
8871 ctlData.fBootable);
8872 if (FAILED(rc)) return rc;
8873
8874 mStorageControllers->push_back(pCtl);
8875
8876 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8877 if (FAILED(rc)) return rc;
8878
8879 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8880 if (FAILED(rc)) return rc;
8881
8882 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8883 if (FAILED(rc)) return rc;
8884
8885 /* Set IDE emulation settings (only for AHCI controller). */
8886 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8887 {
8888 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8889 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8890 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8891 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8892 )
8893 return rc;
8894 }
8895
8896 /* Load the attached devices now. */
8897 rc = loadStorageDevices(pCtl,
8898 ctlData,
8899 puuidRegistry,
8900 puuidSnapshot);
8901 if (FAILED(rc)) return rc;
8902 }
8903
8904 return S_OK;
8905}
8906
8907/**
8908 * Called from loadStorageControllers for a controller's devices.
8909 *
8910 * @param aStorageController
8911 * @param data
8912 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8913 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8914 * @return
8915 */
8916HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8917 const settings::StorageController &data,
8918 const Guid *puuidRegistry,
8919 const Guid *puuidSnapshot)
8920{
8921 HRESULT rc = S_OK;
8922
8923 /* paranoia: detect duplicate attachments */
8924 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8925 it != data.llAttachedDevices.end();
8926 ++it)
8927 {
8928 const settings::AttachedDevice &ad = *it;
8929
8930 for (settings::AttachedDevicesList::const_iterator it2 = it;
8931 it2 != data.llAttachedDevices.end();
8932 ++it2)
8933 {
8934 if (it == it2)
8935 continue;
8936
8937 const settings::AttachedDevice &ad2 = *it2;
8938
8939 if ( ad.lPort == ad2.lPort
8940 && ad.lDevice == ad2.lDevice)
8941 {
8942 return setError(E_FAIL,
8943 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8944 aStorageController->getName().c_str(),
8945 ad.lPort,
8946 ad.lDevice,
8947 mUserData->s.strName.c_str());
8948 }
8949 }
8950 }
8951
8952 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8953 it != data.llAttachedDevices.end();
8954 ++it)
8955 {
8956 const settings::AttachedDevice &dev = *it;
8957 ComObjPtr<Medium> medium;
8958
8959 switch (dev.deviceType)
8960 {
8961 case DeviceType_Floppy:
8962 case DeviceType_DVD:
8963 if (dev.strHostDriveSrc.isNotEmpty())
8964 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8965 else
8966 rc = mParent->findRemoveableMedium(dev.deviceType,
8967 dev.uuid,
8968 false /* fRefresh */,
8969 false /* aSetError */,
8970 medium);
8971 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8972 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8973 rc = S_OK;
8974 break;
8975
8976 case DeviceType_HardDisk:
8977 {
8978 /* find a hard disk by UUID */
8979 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8980 if (FAILED(rc))
8981 {
8982 if (isSnapshotMachine())
8983 {
8984 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8985 // so the user knows that the bad disk is in a snapshot somewhere
8986 com::ErrorInfo info;
8987 return setError(E_FAIL,
8988 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8989 puuidSnapshot->raw(),
8990 info.getText().raw());
8991 }
8992 else
8993 return rc;
8994 }
8995
8996 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8997
8998 if (medium->getType() == MediumType_Immutable)
8999 {
9000 if (isSnapshotMachine())
9001 return setError(E_FAIL,
9002 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9003 "of the virtual machine '%s' ('%s')"),
9004 medium->getLocationFull().c_str(),
9005 dev.uuid.raw(),
9006 puuidSnapshot->raw(),
9007 mUserData->s.strName.c_str(),
9008 mData->m_strConfigFileFull.c_str());
9009
9010 return setError(E_FAIL,
9011 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9012 medium->getLocationFull().c_str(),
9013 dev.uuid.raw(),
9014 mUserData->s.strName.c_str(),
9015 mData->m_strConfigFileFull.c_str());
9016 }
9017
9018 if (medium->getType() == MediumType_MultiAttach)
9019 {
9020 if (isSnapshotMachine())
9021 return setError(E_FAIL,
9022 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9023 "of the virtual machine '%s' ('%s')"),
9024 medium->getLocationFull().c_str(),
9025 dev.uuid.raw(),
9026 puuidSnapshot->raw(),
9027 mUserData->s.strName.c_str(),
9028 mData->m_strConfigFileFull.c_str());
9029
9030 return setError(E_FAIL,
9031 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9032 medium->getLocationFull().c_str(),
9033 dev.uuid.raw(),
9034 mUserData->s.strName.c_str(),
9035 mData->m_strConfigFileFull.c_str());
9036 }
9037
9038 if ( !isSnapshotMachine()
9039 && medium->getChildren().size() != 0
9040 )
9041 return setError(E_FAIL,
9042 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9043 "because it has %d differencing child hard disks"),
9044 medium->getLocationFull().c_str(),
9045 dev.uuid.raw(),
9046 mUserData->s.strName.c_str(),
9047 mData->m_strConfigFileFull.c_str(),
9048 medium->getChildren().size());
9049
9050 if (findAttachment(mMediaData->mAttachments,
9051 medium))
9052 return setError(E_FAIL,
9053 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9054 medium->getLocationFull().c_str(),
9055 dev.uuid.raw(),
9056 mUserData->s.strName.c_str(),
9057 mData->m_strConfigFileFull.c_str());
9058
9059 break;
9060 }
9061
9062 default:
9063 return setError(E_FAIL,
9064 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9065 medium->getLocationFull().c_str(),
9066 mUserData->s.strName.c_str(),
9067 mData->m_strConfigFileFull.c_str());
9068 }
9069
9070 if (FAILED(rc))
9071 break;
9072
9073 /* Bandwidth groups are loaded at this point. */
9074 ComObjPtr<BandwidthGroup> pBwGroup;
9075
9076 if (!dev.strBwGroup.isEmpty())
9077 {
9078 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9079 if (FAILED(rc))
9080 return setError(E_FAIL,
9081 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9082 medium->getLocationFull().c_str(),
9083 dev.strBwGroup.c_str(),
9084 mUserData->s.strName.c_str(),
9085 mData->m_strConfigFileFull.c_str());
9086 pBwGroup->reference();
9087 }
9088
9089 const Bstr controllerName = aStorageController->getName();
9090 ComObjPtr<MediumAttachment> pAttachment;
9091 pAttachment.createObject();
9092 rc = pAttachment->init(this,
9093 medium,
9094 controllerName,
9095 dev.lPort,
9096 dev.lDevice,
9097 dev.deviceType,
9098 false,
9099 dev.fPassThrough,
9100 dev.fTempEject,
9101 dev.fNonRotational,
9102 dev.fDiscard,
9103 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9104 if (FAILED(rc)) break;
9105
9106 /* associate the medium with this machine and snapshot */
9107 if (!medium.isNull())
9108 {
9109 AutoCaller medCaller(medium);
9110 if (FAILED(medCaller.rc())) return medCaller.rc();
9111 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9112
9113 if (isSnapshotMachine())
9114 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9115 else
9116 rc = medium->addBackReference(mData->mUuid);
9117 /* If the medium->addBackReference fails it sets an appropriate
9118 * error message, so no need to do any guesswork here. */
9119
9120 if (puuidRegistry)
9121 // caller wants registry ID to be set on all attached media (OVF import case)
9122 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9123 }
9124
9125 if (FAILED(rc))
9126 break;
9127
9128 /* back up mMediaData to let registeredInit() properly rollback on failure
9129 * (= limited accessibility) */
9130 setModified(IsModified_Storage);
9131 mMediaData.backup();
9132 mMediaData->mAttachments.push_back(pAttachment);
9133 }
9134
9135 return rc;
9136}
9137
9138/**
9139 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9140 *
9141 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9142 * @param aSnapshot where to return the found snapshot
9143 * @param aSetError true to set extended error info on failure
9144 */
9145HRESULT Machine::findSnapshotById(const Guid &aId,
9146 ComObjPtr<Snapshot> &aSnapshot,
9147 bool aSetError /* = false */)
9148{
9149 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9150
9151 if (!mData->mFirstSnapshot)
9152 {
9153 if (aSetError)
9154 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9155 return E_FAIL;
9156 }
9157
9158 if (aId.isZero())
9159 aSnapshot = mData->mFirstSnapshot;
9160 else
9161 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9162
9163 if (!aSnapshot)
9164 {
9165 if (aSetError)
9166 return setError(E_FAIL,
9167 tr("Could not find a snapshot with UUID {%s}"),
9168 aId.toString().c_str());
9169 return E_FAIL;
9170 }
9171
9172 return S_OK;
9173}
9174
9175/**
9176 * Returns the snapshot with the given name or fails of no such snapshot.
9177 *
9178 * @param aName snapshot name to find
9179 * @param aSnapshot where to return the found snapshot
9180 * @param aSetError true to set extended error info on failure
9181 */
9182HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9183 ComObjPtr<Snapshot> &aSnapshot,
9184 bool aSetError /* = false */)
9185{
9186 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9187
9188 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9189
9190 if (!mData->mFirstSnapshot)
9191 {
9192 if (aSetError)
9193 return setError(VBOX_E_OBJECT_NOT_FOUND,
9194 tr("This machine does not have any snapshots"));
9195 return VBOX_E_OBJECT_NOT_FOUND;
9196 }
9197
9198 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9199
9200 if (!aSnapshot)
9201 {
9202 if (aSetError)
9203 return setError(VBOX_E_OBJECT_NOT_FOUND,
9204 tr("Could not find a snapshot named '%s'"), strName.c_str());
9205 return VBOX_E_OBJECT_NOT_FOUND;
9206 }
9207
9208 return S_OK;
9209}
9210
9211/**
9212 * Returns a storage controller object with the given name.
9213 *
9214 * @param aName storage controller name to find
9215 * @param aStorageController where to return the found storage controller
9216 * @param aSetError true to set extended error info on failure
9217 */
9218HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9219 ComObjPtr<StorageController> &aStorageController,
9220 bool aSetError /* = false */)
9221{
9222 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9223
9224 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9225 it != mStorageControllers->end();
9226 ++it)
9227 {
9228 if ((*it)->getName() == aName)
9229 {
9230 aStorageController = (*it);
9231 return S_OK;
9232 }
9233 }
9234
9235 if (aSetError)
9236 return setError(VBOX_E_OBJECT_NOT_FOUND,
9237 tr("Could not find a storage controller named '%s'"),
9238 aName.c_str());
9239 return VBOX_E_OBJECT_NOT_FOUND;
9240}
9241
9242HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9243 MediaData::AttachmentList &atts)
9244{
9245 AutoCaller autoCaller(this);
9246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9247
9248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9249
9250 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9251 it != mMediaData->mAttachments.end();
9252 ++it)
9253 {
9254 const ComObjPtr<MediumAttachment> &pAtt = *it;
9255
9256 // should never happen, but deal with NULL pointers in the list.
9257 AssertStmt(!pAtt.isNull(), continue);
9258
9259 // getControllerName() needs caller+read lock
9260 AutoCaller autoAttCaller(pAtt);
9261 if (FAILED(autoAttCaller.rc()))
9262 {
9263 atts.clear();
9264 return autoAttCaller.rc();
9265 }
9266 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9267
9268 if (pAtt->getControllerName() == aName)
9269 atts.push_back(pAtt);
9270 }
9271
9272 return S_OK;
9273}
9274
9275/**
9276 * Helper for #saveSettings. Cares about renaming the settings directory and
9277 * file if the machine name was changed and about creating a new settings file
9278 * if this is a new machine.
9279 *
9280 * @note Must be never called directly but only from #saveSettings().
9281 */
9282HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9283{
9284 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9285
9286 HRESULT rc = S_OK;
9287
9288 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9289
9290 /// @todo need to handle primary group change, too
9291
9292 /* attempt to rename the settings file if machine name is changed */
9293 if ( mUserData->s.fNameSync
9294 && mUserData.isBackedUp()
9295 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9296 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9297 )
9298 {
9299 bool dirRenamed = false;
9300 bool fileRenamed = false;
9301
9302 Utf8Str configFile, newConfigFile;
9303 Utf8Str configFilePrev, newConfigFilePrev;
9304 Utf8Str configDir, newConfigDir;
9305
9306 do
9307 {
9308 int vrc = VINF_SUCCESS;
9309
9310 Utf8Str name = mUserData.backedUpData()->s.strName;
9311 Utf8Str newName = mUserData->s.strName;
9312 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9313 if (group == "/")
9314 group.setNull();
9315 Utf8Str newGroup = mUserData->s.llGroups.front();
9316 if (newGroup == "/")
9317 newGroup.setNull();
9318
9319 configFile = mData->m_strConfigFileFull;
9320
9321 /* first, rename the directory if it matches the group and machine name */
9322 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9323 group.c_str(), RTPATH_DELIMITER, name.c_str());
9324 /** @todo hack, make somehow use of ComposeMachineFilename */
9325 if (mUserData->s.fDirectoryIncludesUUID)
9326 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9327 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9328 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9329 /** @todo hack, make somehow use of ComposeMachineFilename */
9330 if (mUserData->s.fDirectoryIncludesUUID)
9331 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9332 configDir = configFile;
9333 configDir.stripFilename();
9334 newConfigDir = configDir;
9335 if ( configDir.length() >= groupPlusName.length()
9336 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9337 {
9338 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9339 Utf8Str newConfigBaseDir(newConfigDir);
9340 newConfigDir.append(newGroupPlusName);
9341 /* consistency: use \ if appropriate on the platform */
9342 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9343 /* new dir and old dir cannot be equal here because of 'if'
9344 * above and because name != newName */
9345 Assert(configDir != newConfigDir);
9346 if (!fSettingsFileIsNew)
9347 {
9348 /* perform real rename only if the machine is not new */
9349 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9350 if ( vrc == VERR_FILE_NOT_FOUND
9351 || vrc == VERR_PATH_NOT_FOUND)
9352 {
9353 /* create the parent directory, then retry renaming */
9354 Utf8Str parent(newConfigDir);
9355 parent.stripFilename();
9356 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9357 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9358 }
9359 if (RT_FAILURE(vrc))
9360 {
9361 rc = setError(E_FAIL,
9362 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9363 configDir.c_str(),
9364 newConfigDir.c_str(),
9365 vrc);
9366 break;
9367 }
9368 /* delete subdirectories which are no longer needed */
9369 Utf8Str dir(configDir);
9370 dir.stripFilename();
9371 while (dir != newConfigBaseDir && dir != ".")
9372 {
9373 vrc = RTDirRemove(dir.c_str());
9374 if (RT_FAILURE(vrc))
9375 break;
9376 dir.stripFilename();
9377 }
9378 dirRenamed = true;
9379 }
9380 }
9381
9382 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9383 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9384
9385 /* then try to rename the settings file itself */
9386 if (newConfigFile != configFile)
9387 {
9388 /* get the path to old settings file in renamed directory */
9389 configFile = Utf8StrFmt("%s%c%s",
9390 newConfigDir.c_str(),
9391 RTPATH_DELIMITER,
9392 RTPathFilename(configFile.c_str()));
9393 if (!fSettingsFileIsNew)
9394 {
9395 /* perform real rename only if the machine is not new */
9396 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9397 if (RT_FAILURE(vrc))
9398 {
9399 rc = setError(E_FAIL,
9400 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9401 configFile.c_str(),
9402 newConfigFile.c_str(),
9403 vrc);
9404 break;
9405 }
9406 fileRenamed = true;
9407 configFilePrev = configFile;
9408 configFilePrev += "-prev";
9409 newConfigFilePrev = newConfigFile;
9410 newConfigFilePrev += "-prev";
9411 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9412 }
9413 }
9414
9415 // update m_strConfigFileFull amd mConfigFile
9416 mData->m_strConfigFileFull = newConfigFile;
9417 // compute the relative path too
9418 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9419
9420 // store the old and new so that VirtualBox::saveSettings() can update
9421 // the media registry
9422 if ( mData->mRegistered
9423 && configDir != newConfigDir)
9424 {
9425 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9426
9427 if (pfNeedsGlobalSaveSettings)
9428 *pfNeedsGlobalSaveSettings = true;
9429 }
9430
9431 // in the saved state file path, replace the old directory with the new directory
9432 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9433 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9434
9435 // and do the same thing for the saved state file paths of all the online snapshots
9436 if (mData->mFirstSnapshot)
9437 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9438 newConfigDir.c_str());
9439 }
9440 while (0);
9441
9442 if (FAILED(rc))
9443 {
9444 /* silently try to rename everything back */
9445 if (fileRenamed)
9446 {
9447 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9448 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9449 }
9450 if (dirRenamed)
9451 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9452 }
9453
9454 if (FAILED(rc)) return rc;
9455 }
9456
9457 if (fSettingsFileIsNew)
9458 {
9459 /* create a virgin config file */
9460 int vrc = VINF_SUCCESS;
9461
9462 /* ensure the settings directory exists */
9463 Utf8Str path(mData->m_strConfigFileFull);
9464 path.stripFilename();
9465 if (!RTDirExists(path.c_str()))
9466 {
9467 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9468 if (RT_FAILURE(vrc))
9469 {
9470 return setError(E_FAIL,
9471 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9472 path.c_str(),
9473 vrc);
9474 }
9475 }
9476
9477 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9478 path = Utf8Str(mData->m_strConfigFileFull);
9479 RTFILE f = NIL_RTFILE;
9480 vrc = RTFileOpen(&f, path.c_str(),
9481 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9482 if (RT_FAILURE(vrc))
9483 return setError(E_FAIL,
9484 tr("Could not create the settings file '%s' (%Rrc)"),
9485 path.c_str(),
9486 vrc);
9487 RTFileClose(f);
9488 }
9489
9490 return rc;
9491}
9492
9493/**
9494 * Saves and commits machine data, user data and hardware data.
9495 *
9496 * Note that on failure, the data remains uncommitted.
9497 *
9498 * @a aFlags may combine the following flags:
9499 *
9500 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9501 * Used when saving settings after an operation that makes them 100%
9502 * correspond to the settings from the current snapshot.
9503 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9504 * #isReallyModified() returns false. This is necessary for cases when we
9505 * change machine data directly, not through the backup()/commit() mechanism.
9506 * - SaveS_Force: settings will be saved without doing a deep compare of the
9507 * settings structures. This is used when this is called because snapshots
9508 * have changed to avoid the overhead of the deep compare.
9509 *
9510 * @note Must be called from under this object's write lock. Locks children for
9511 * writing.
9512 *
9513 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9514 * initialized to false and that will be set to true by this function if
9515 * the caller must invoke VirtualBox::saveSettings() because the global
9516 * settings have changed. This will happen if a machine rename has been
9517 * saved and the global machine and media registries will therefore need
9518 * updating.
9519 */
9520HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9521 int aFlags /*= 0*/)
9522{
9523 LogFlowThisFuncEnter();
9524
9525 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9526
9527 /* make sure child objects are unable to modify the settings while we are
9528 * saving them */
9529 ensureNoStateDependencies();
9530
9531 AssertReturn(!isSnapshotMachine(),
9532 E_FAIL);
9533
9534 HRESULT rc = S_OK;
9535 bool fNeedsWrite = false;
9536
9537 /* First, prepare to save settings. It will care about renaming the
9538 * settings directory and file if the machine name was changed and about
9539 * creating a new settings file if this is a new machine. */
9540 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9541 if (FAILED(rc)) return rc;
9542
9543 // keep a pointer to the current settings structures
9544 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9545 settings::MachineConfigFile *pNewConfig = NULL;
9546
9547 try
9548 {
9549 // make a fresh one to have everyone write stuff into
9550 pNewConfig = new settings::MachineConfigFile(NULL);
9551 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9552
9553 // now go and copy all the settings data from COM to the settings structures
9554 // (this calles saveSettings() on all the COM objects in the machine)
9555 copyMachineDataToSettings(*pNewConfig);
9556
9557 if (aFlags & SaveS_ResetCurStateModified)
9558 {
9559 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9560 mData->mCurrentStateModified = FALSE;
9561 fNeedsWrite = true; // always, no need to compare
9562 }
9563 else if (aFlags & SaveS_Force)
9564 {
9565 fNeedsWrite = true; // always, no need to compare
9566 }
9567 else
9568 {
9569 if (!mData->mCurrentStateModified)
9570 {
9571 // do a deep compare of the settings that we just saved with the settings
9572 // previously stored in the config file; this invokes MachineConfigFile::operator==
9573 // which does a deep compare of all the settings, which is expensive but less expensive
9574 // than writing out XML in vain
9575 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9576
9577 // could still be modified if any settings changed
9578 mData->mCurrentStateModified = fAnySettingsChanged;
9579
9580 fNeedsWrite = fAnySettingsChanged;
9581 }
9582 else
9583 fNeedsWrite = true;
9584 }
9585
9586 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9587
9588 if (fNeedsWrite)
9589 // now spit it all out!
9590 pNewConfig->write(mData->m_strConfigFileFull);
9591
9592 mData->pMachineConfigFile = pNewConfig;
9593 delete pOldConfig;
9594 commit();
9595
9596 // after saving settings, we are no longer different from the XML on disk
9597 mData->flModifications = 0;
9598 }
9599 catch (HRESULT err)
9600 {
9601 // we assume that error info is set by the thrower
9602 rc = err;
9603
9604 // restore old config
9605 delete pNewConfig;
9606 mData->pMachineConfigFile = pOldConfig;
9607 }
9608 catch (...)
9609 {
9610 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9611 }
9612
9613 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9614 {
9615 /* Fire the data change event, even on failure (since we've already
9616 * committed all data). This is done only for SessionMachines because
9617 * mutable Machine instances are always not registered (i.e. private
9618 * to the client process that creates them) and thus don't need to
9619 * inform callbacks. */
9620 if (isSessionMachine())
9621 mParent->onMachineDataChange(mData->mUuid);
9622 }
9623
9624 LogFlowThisFunc(("rc=%08X\n", rc));
9625 LogFlowThisFuncLeave();
9626 return rc;
9627}
9628
9629/**
9630 * Implementation for saving the machine settings into the given
9631 * settings::MachineConfigFile instance. This copies machine extradata
9632 * from the previous machine config file in the instance data, if any.
9633 *
9634 * This gets called from two locations:
9635 *
9636 * -- Machine::saveSettings(), during the regular XML writing;
9637 *
9638 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9639 * exported to OVF and we write the VirtualBox proprietary XML
9640 * into a <vbox:Machine> tag.
9641 *
9642 * This routine fills all the fields in there, including snapshots, *except*
9643 * for the following:
9644 *
9645 * -- fCurrentStateModified. There is some special logic associated with that.
9646 *
9647 * The caller can then call MachineConfigFile::write() or do something else
9648 * with it.
9649 *
9650 * Caller must hold the machine lock!
9651 *
9652 * This throws XML errors and HRESULT, so the caller must have a catch block!
9653 */
9654void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9655{
9656 // deep copy extradata
9657 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9658
9659 config.uuid = mData->mUuid;
9660
9661 // copy name, description, OS type, teleport, UTC etc.
9662 config.machineUserData = mUserData->s;
9663
9664 if ( mData->mMachineState == MachineState_Saved
9665 || mData->mMachineState == MachineState_Restoring
9666 // when deleting a snapshot we may or may not have a saved state in the current state,
9667 // so let's not assert here please
9668 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9669 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9670 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9671 && (!mSSData->strStateFilePath.isEmpty())
9672 )
9673 )
9674 {
9675 Assert(!mSSData->strStateFilePath.isEmpty());
9676 /* try to make the file name relative to the settings file dir */
9677 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9678 }
9679 else
9680 {
9681 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9682 config.strStateFile.setNull();
9683 }
9684
9685 if (mData->mCurrentSnapshot)
9686 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9687 else
9688 config.uuidCurrentSnapshot.clear();
9689
9690 config.timeLastStateChange = mData->mLastStateChange;
9691 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9692 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9693
9694 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9695 if (FAILED(rc)) throw rc;
9696
9697 rc = saveStorageControllers(config.storageMachine);
9698 if (FAILED(rc)) throw rc;
9699
9700 // save machine's media registry if this is VirtualBox 4.0 or later
9701 if (config.canHaveOwnMediaRegistry())
9702 {
9703 // determine machine folder
9704 Utf8Str strMachineFolder = getSettingsFileFull();
9705 strMachineFolder.stripFilename();
9706 mParent->saveMediaRegistry(config.mediaRegistry,
9707 getId(), // only media with registry ID == machine UUID
9708 strMachineFolder);
9709 // this throws HRESULT
9710 }
9711
9712 // save snapshots
9713 rc = saveAllSnapshots(config);
9714 if (FAILED(rc)) throw rc;
9715}
9716
9717/**
9718 * Saves all snapshots of the machine into the given machine config file. Called
9719 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9720 * @param config
9721 * @return
9722 */
9723HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9724{
9725 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9726
9727 HRESULT rc = S_OK;
9728
9729 try
9730 {
9731 config.llFirstSnapshot.clear();
9732
9733 if (mData->mFirstSnapshot)
9734 {
9735 settings::Snapshot snapNew;
9736 config.llFirstSnapshot.push_back(snapNew);
9737
9738 // get reference to the fresh copy of the snapshot on the list and
9739 // work on that copy directly to avoid excessive copying later
9740 settings::Snapshot &snap = config.llFirstSnapshot.front();
9741
9742 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9743 if (FAILED(rc)) throw rc;
9744 }
9745
9746// if (mType == IsSessionMachine)
9747// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9748
9749 }
9750 catch (HRESULT err)
9751 {
9752 /* we assume that error info is set by the thrower */
9753 rc = err;
9754 }
9755 catch (...)
9756 {
9757 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9758 }
9759
9760 return rc;
9761}
9762
9763/**
9764 * Saves the VM hardware configuration. It is assumed that the
9765 * given node is empty.
9766 *
9767 * @param data Reference to the settings object for the hardware config.
9768 * @param pDbg Pointer to the settings object for the debugging config
9769 * which happens to live in mHWData.
9770 * @param pAutostart Pointer to the settings object for the autostart config
9771 * which happens to live in mHWData.
9772 */
9773HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9774 settings::Autostart *pAutostart)
9775{
9776 HRESULT rc = S_OK;
9777
9778 try
9779 {
9780 /* The hardware version attribute (optional).
9781 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9782 if ( mHWData->mHWVersion == "1"
9783 && mSSData->strStateFilePath.isEmpty()
9784 )
9785 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. */
9786
9787 data.strVersion = mHWData->mHWVersion;
9788 data.uuid = mHWData->mHardwareUUID;
9789
9790 // CPU
9791 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9792 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9793 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9794 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9795 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9796 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9797 data.fPAE = !!mHWData->mPAEEnabled;
9798 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9799
9800 /* Standard and Extended CPUID leafs. */
9801 data.llCpuIdLeafs.clear();
9802 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9803 {
9804 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9805 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9806 }
9807 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9808 {
9809 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9810 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9811 }
9812
9813 data.cCPUs = mHWData->mCPUCount;
9814 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9815 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9816
9817 data.llCpus.clear();
9818 if (data.fCpuHotPlug)
9819 {
9820 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9821 {
9822 if (mHWData->mCPUAttached[idx])
9823 {
9824 settings::Cpu cpu;
9825 cpu.ulId = idx;
9826 data.llCpus.push_back(cpu);
9827 }
9828 }
9829 }
9830
9831 // memory
9832 data.ulMemorySizeMB = mHWData->mMemorySize;
9833 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9834
9835 // firmware
9836 data.firmwareType = mHWData->mFirmwareType;
9837
9838 // HID
9839 data.pointingHIDType = mHWData->mPointingHIDType;
9840 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9841
9842 // chipset
9843 data.chipsetType = mHWData->mChipsetType;
9844
9845 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9846 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9847
9848 // HPET
9849 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9850
9851 // boot order
9852 data.mapBootOrder.clear();
9853 for (size_t i = 0;
9854 i < RT_ELEMENTS(mHWData->mBootOrder);
9855 ++i)
9856 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9857
9858 // display
9859 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9860 data.cMonitors = mHWData->mMonitorCount;
9861 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9862 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9863 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9864 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9865 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9866 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9867
9868 /* VRDEServer settings (optional) */
9869 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9870 if (FAILED(rc)) throw rc;
9871
9872 /* BIOS (required) */
9873 rc = mBIOSSettings->saveSettings(data.biosSettings);
9874 if (FAILED(rc)) throw rc;
9875
9876 /* USB Controller (required) */
9877 rc = mUSBController->saveSettings(data.usbController);
9878 if (FAILED(rc)) throw rc;
9879
9880 /* Network adapters (required) */
9881 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9882 data.llNetworkAdapters.clear();
9883 /* Write out only the nominal number of network adapters for this
9884 * chipset type. Since Machine::commit() hasn't been called there
9885 * may be extra NIC settings in the vector. */
9886 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9887 {
9888 settings::NetworkAdapter nic;
9889 nic.ulSlot = slot;
9890 /* paranoia check... must not be NULL, but must not crash either. */
9891 if (mNetworkAdapters[slot])
9892 {
9893 rc = mNetworkAdapters[slot]->saveSettings(nic);
9894 if (FAILED(rc)) throw rc;
9895
9896 data.llNetworkAdapters.push_back(nic);
9897 }
9898 }
9899
9900 /* Serial ports */
9901 data.llSerialPorts.clear();
9902 for (ULONG slot = 0;
9903 slot < RT_ELEMENTS(mSerialPorts);
9904 ++slot)
9905 {
9906 settings::SerialPort s;
9907 s.ulSlot = slot;
9908 rc = mSerialPorts[slot]->saveSettings(s);
9909 if (FAILED(rc)) return rc;
9910
9911 data.llSerialPorts.push_back(s);
9912 }
9913
9914 /* Parallel ports */
9915 data.llParallelPorts.clear();
9916 for (ULONG slot = 0;
9917 slot < RT_ELEMENTS(mParallelPorts);
9918 ++slot)
9919 {
9920 settings::ParallelPort p;
9921 p.ulSlot = slot;
9922 rc = mParallelPorts[slot]->saveSettings(p);
9923 if (FAILED(rc)) return rc;
9924
9925 data.llParallelPorts.push_back(p);
9926 }
9927
9928 /* Audio adapter */
9929 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9930 if (FAILED(rc)) return rc;
9931
9932 /* Shared folders */
9933 data.llSharedFolders.clear();
9934 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9935 it != mHWData->mSharedFolders.end();
9936 ++it)
9937 {
9938 SharedFolder *pSF = *it;
9939 AutoCaller sfCaller(pSF);
9940 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9941 settings::SharedFolder sf;
9942 sf.strName = pSF->getName();
9943 sf.strHostPath = pSF->getHostPath();
9944 sf.fWritable = !!pSF->isWritable();
9945 sf.fAutoMount = !!pSF->isAutoMounted();
9946
9947 data.llSharedFolders.push_back(sf);
9948 }
9949
9950 // clipboard
9951 data.clipboardMode = mHWData->mClipboardMode;
9952
9953 // drag'n'drop
9954 data.dragAndDropMode = mHWData->mDragAndDropMode;
9955
9956 /* Guest */
9957 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9958
9959 // IO settings
9960 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9961 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9962
9963 /* BandwidthControl (required) */
9964 rc = mBandwidthControl->saveSettings(data.ioSettings);
9965 if (FAILED(rc)) throw rc;
9966
9967 /* Host PCI devices */
9968 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9969 it != mHWData->mPCIDeviceAssignments.end();
9970 ++it)
9971 {
9972 ComObjPtr<PCIDeviceAttachment> pda = *it;
9973 settings::HostPCIDeviceAttachment hpda;
9974
9975 rc = pda->saveSettings(hpda);
9976 if (FAILED(rc)) throw rc;
9977
9978 data.pciAttachments.push_back(hpda);
9979 }
9980
9981
9982 // guest properties
9983 data.llGuestProperties.clear();
9984#ifdef VBOX_WITH_GUEST_PROPS
9985 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
9986 it != mHWData->mGuestProperties.end();
9987 ++it)
9988 {
9989 HWData::GuestProperty property = it->second;
9990
9991 /* Remove transient guest properties at shutdown unless we
9992 * are saving state */
9993 if ( ( mData->mMachineState == MachineState_PoweredOff
9994 || mData->mMachineState == MachineState_Aborted
9995 || mData->mMachineState == MachineState_Teleported)
9996 && ( property.mFlags & guestProp::TRANSIENT
9997 || property.mFlags & guestProp::TRANSRESET))
9998 continue;
9999 settings::GuestProperty prop;
10000 prop.strName = it->first;
10001 prop.strValue = property.strValue;
10002 prop.timestamp = property.mTimestamp;
10003 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10004 guestProp::writeFlags(property.mFlags, szFlags);
10005 prop.strFlags = szFlags;
10006
10007 data.llGuestProperties.push_back(prop);
10008 }
10009
10010 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10011 /* I presume this doesn't require a backup(). */
10012 mData->mGuestPropertiesModified = FALSE;
10013#endif /* VBOX_WITH_GUEST_PROPS defined */
10014
10015 *pDbg = mHWData->mDebugging;
10016 *pAutostart = mHWData->mAutostart;
10017
10018 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10019 }
10020 catch(std::bad_alloc &)
10021 {
10022 return E_OUTOFMEMORY;
10023 }
10024
10025 AssertComRC(rc);
10026 return rc;
10027}
10028
10029/**
10030 * Saves the storage controller configuration.
10031 *
10032 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10033 */
10034HRESULT Machine::saveStorageControllers(settings::Storage &data)
10035{
10036 data.llStorageControllers.clear();
10037
10038 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10039 it != mStorageControllers->end();
10040 ++it)
10041 {
10042 HRESULT rc;
10043 ComObjPtr<StorageController> pCtl = *it;
10044
10045 settings::StorageController ctl;
10046 ctl.strName = pCtl->getName();
10047 ctl.controllerType = pCtl->getControllerType();
10048 ctl.storageBus = pCtl->getStorageBus();
10049 ctl.ulInstance = pCtl->getInstance();
10050 ctl.fBootable = pCtl->getBootable();
10051
10052 /* Save the port count. */
10053 ULONG portCount;
10054 rc = pCtl->COMGETTER(PortCount)(&portCount);
10055 ComAssertComRCRet(rc, rc);
10056 ctl.ulPortCount = portCount;
10057
10058 /* Save fUseHostIOCache */
10059 BOOL fUseHostIOCache;
10060 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10061 ComAssertComRCRet(rc, rc);
10062 ctl.fUseHostIOCache = !!fUseHostIOCache;
10063
10064 /* Save IDE emulation settings. */
10065 if (ctl.controllerType == StorageControllerType_IntelAhci)
10066 {
10067 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10068 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10069 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10070 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10071 )
10072 ComAssertComRCRet(rc, rc);
10073 }
10074
10075 /* save the devices now. */
10076 rc = saveStorageDevices(pCtl, ctl);
10077 ComAssertComRCRet(rc, rc);
10078
10079 data.llStorageControllers.push_back(ctl);
10080 }
10081
10082 return S_OK;
10083}
10084
10085/**
10086 * Saves the hard disk configuration.
10087 */
10088HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10089 settings::StorageController &data)
10090{
10091 MediaData::AttachmentList atts;
10092
10093 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10094 if (FAILED(rc)) return rc;
10095
10096 data.llAttachedDevices.clear();
10097 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10098 it != atts.end();
10099 ++it)
10100 {
10101 settings::AttachedDevice dev;
10102
10103 MediumAttachment *pAttach = *it;
10104 Medium *pMedium = pAttach->getMedium();
10105
10106 dev.deviceType = pAttach->getType();
10107 dev.lPort = pAttach->getPort();
10108 dev.lDevice = pAttach->getDevice();
10109 if (pMedium)
10110 {
10111 if (pMedium->isHostDrive())
10112 dev.strHostDriveSrc = pMedium->getLocationFull();
10113 else
10114 dev.uuid = pMedium->getId();
10115 dev.fPassThrough = pAttach->getPassthrough();
10116 dev.fTempEject = pAttach->getTempEject();
10117 dev.fNonRotational = pAttach->getNonRotational();
10118 dev.fDiscard = pAttach->getDiscard();
10119 }
10120
10121 dev.strBwGroup = pAttach->getBandwidthGroup();
10122
10123 data.llAttachedDevices.push_back(dev);
10124 }
10125
10126 return S_OK;
10127}
10128
10129/**
10130 * Saves machine state settings as defined by aFlags
10131 * (SaveSTS_* values).
10132 *
10133 * @param aFlags Combination of SaveSTS_* flags.
10134 *
10135 * @note Locks objects for writing.
10136 */
10137HRESULT Machine::saveStateSettings(int aFlags)
10138{
10139 if (aFlags == 0)
10140 return S_OK;
10141
10142 AutoCaller autoCaller(this);
10143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10144
10145 /* This object's write lock is also necessary to serialize file access
10146 * (prevent concurrent reads and writes) */
10147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10148
10149 HRESULT rc = S_OK;
10150
10151 Assert(mData->pMachineConfigFile);
10152
10153 try
10154 {
10155 if (aFlags & SaveSTS_CurStateModified)
10156 mData->pMachineConfigFile->fCurrentStateModified = true;
10157
10158 if (aFlags & SaveSTS_StateFilePath)
10159 {
10160 if (!mSSData->strStateFilePath.isEmpty())
10161 /* try to make the file name relative to the settings file dir */
10162 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10163 else
10164 mData->pMachineConfigFile->strStateFile.setNull();
10165 }
10166
10167 if (aFlags & SaveSTS_StateTimeStamp)
10168 {
10169 Assert( mData->mMachineState != MachineState_Aborted
10170 || mSSData->strStateFilePath.isEmpty());
10171
10172 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10173
10174 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10175//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10176 }
10177
10178 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10179 }
10180 catch (...)
10181 {
10182 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10183 }
10184
10185 return rc;
10186}
10187
10188/**
10189 * Ensures that the given medium is added to a media registry. If this machine
10190 * was created with 4.0 or later, then the machine registry is used. Otherwise
10191 * the global VirtualBox media registry is used.
10192 *
10193 * Caller must NOT hold machine lock, media tree or any medium locks!
10194 *
10195 * @param pMedium
10196 */
10197void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10198{
10199 /* Paranoia checks: do not hold machine or media tree locks. */
10200 AssertReturnVoid(!isWriteLockOnCurrentThread());
10201 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10202
10203 ComObjPtr<Medium> pBase;
10204 {
10205 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10206 pBase = pMedium->getBase();
10207 }
10208
10209 /* Paranoia checks: do not hold medium locks. */
10210 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10211 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10212
10213 // decide which medium registry to use now that the medium is attached:
10214 Guid uuid;
10215 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10216 // machine XML is VirtualBox 4.0 or higher:
10217 uuid = getId(); // machine UUID
10218 else
10219 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10220
10221 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10222 mParent->markRegistryModified(uuid);
10223
10224 /* For more complex hard disk structures it can happen that the base
10225 * medium isn't yet associated with any medium registry. Do that now. */
10226 if (pMedium != pBase)
10227 {
10228 if (pBase->addRegistry(uuid, true /* fRecurse */))
10229 mParent->markRegistryModified(uuid);
10230 }
10231}
10232
10233/**
10234 * Creates differencing hard disks for all normal hard disks attached to this
10235 * machine and a new set of attachments to refer to created disks.
10236 *
10237 * Used when taking a snapshot or when deleting the current state. Gets called
10238 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10239 *
10240 * This method assumes that mMediaData contains the original hard disk attachments
10241 * it needs to create diffs for. On success, these attachments will be replaced
10242 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10243 * called to delete created diffs which will also rollback mMediaData and restore
10244 * whatever was backed up before calling this method.
10245 *
10246 * Attachments with non-normal hard disks are left as is.
10247 *
10248 * If @a aOnline is @c false then the original hard disks that require implicit
10249 * diffs will be locked for reading. Otherwise it is assumed that they are
10250 * already locked for writing (when the VM was started). Note that in the latter
10251 * case it is responsibility of the caller to lock the newly created diffs for
10252 * writing if this method succeeds.
10253 *
10254 * @param aProgress Progress object to run (must contain at least as
10255 * many operations left as the number of hard disks
10256 * attached).
10257 * @param aOnline Whether the VM was online prior to this operation.
10258 *
10259 * @note The progress object is not marked as completed, neither on success nor
10260 * on failure. This is a responsibility of the caller.
10261 *
10262 * @note Locks this object and the media tree for writing.
10263 */
10264HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10265 ULONG aWeight,
10266 bool aOnline)
10267{
10268 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10269
10270 AutoCaller autoCaller(this);
10271 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10272
10273 AutoMultiWriteLock2 alock(this->lockHandle(),
10274 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10275
10276 /* must be in a protective state because we release the lock below */
10277 AssertReturn( mData->mMachineState == MachineState_Saving
10278 || mData->mMachineState == MachineState_LiveSnapshotting
10279 || mData->mMachineState == MachineState_RestoringSnapshot
10280 || mData->mMachineState == MachineState_DeletingSnapshot
10281 , E_FAIL);
10282
10283 HRESULT rc = S_OK;
10284
10285 // use appropriate locked media map (online or offline)
10286 MediumLockListMap lockedMediaOffline;
10287 MediumLockListMap *lockedMediaMap;
10288 if (aOnline)
10289 lockedMediaMap = &mData->mSession.mLockedMedia;
10290 else
10291 lockedMediaMap = &lockedMediaOffline;
10292
10293 try
10294 {
10295 if (!aOnline)
10296 {
10297 /* lock all attached hard disks early to detect "in use"
10298 * situations before creating actual diffs */
10299 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10300 it != mMediaData->mAttachments.end();
10301 ++it)
10302 {
10303 MediumAttachment* pAtt = *it;
10304 if (pAtt->getType() == DeviceType_HardDisk)
10305 {
10306 Medium* pMedium = pAtt->getMedium();
10307 Assert(pMedium);
10308
10309 MediumLockList *pMediumLockList(new MediumLockList());
10310 alock.release();
10311 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10312 false /* fMediumLockWrite */,
10313 NULL,
10314 *pMediumLockList);
10315 alock.acquire();
10316 if (FAILED(rc))
10317 {
10318 delete pMediumLockList;
10319 throw rc;
10320 }
10321 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10322 if (FAILED(rc))
10323 {
10324 throw setError(rc,
10325 tr("Collecting locking information for all attached media failed"));
10326 }
10327 }
10328 }
10329
10330 /* Now lock all media. If this fails, nothing is locked. */
10331 alock.release();
10332 rc = lockedMediaMap->Lock();
10333 alock.acquire();
10334 if (FAILED(rc))
10335 {
10336 throw setError(rc,
10337 tr("Locking of attached media failed"));
10338 }
10339 }
10340
10341 /* remember the current list (note that we don't use backup() since
10342 * mMediaData may be already backed up) */
10343 MediaData::AttachmentList atts = mMediaData->mAttachments;
10344
10345 /* start from scratch */
10346 mMediaData->mAttachments.clear();
10347
10348 /* go through remembered attachments and create diffs for normal hard
10349 * disks and attach them */
10350 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10351 it != atts.end();
10352 ++it)
10353 {
10354 MediumAttachment* pAtt = *it;
10355
10356 DeviceType_T devType = pAtt->getType();
10357 Medium* pMedium = pAtt->getMedium();
10358
10359 if ( devType != DeviceType_HardDisk
10360 || pMedium == NULL
10361 || pMedium->getType() != MediumType_Normal)
10362 {
10363 /* copy the attachment as is */
10364
10365 /** @todo the progress object created in Console::TakeSnaphot
10366 * only expects operations for hard disks. Later other
10367 * device types need to show up in the progress as well. */
10368 if (devType == DeviceType_HardDisk)
10369 {
10370 if (pMedium == NULL)
10371 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10372 aWeight); // weight
10373 else
10374 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10375 pMedium->getBase()->getName().c_str()).raw(),
10376 aWeight); // weight
10377 }
10378
10379 mMediaData->mAttachments.push_back(pAtt);
10380 continue;
10381 }
10382
10383 /* need a diff */
10384 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10385 pMedium->getBase()->getName().c_str()).raw(),
10386 aWeight); // weight
10387
10388 Utf8Str strFullSnapshotFolder;
10389 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10390
10391 ComObjPtr<Medium> diff;
10392 diff.createObject();
10393 // store the diff in the same registry as the parent
10394 // (this cannot fail here because we can't create implicit diffs for
10395 // unregistered images)
10396 Guid uuidRegistryParent;
10397 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10398 Assert(fInRegistry); NOREF(fInRegistry);
10399 rc = diff->init(mParent,
10400 pMedium->getPreferredDiffFormat(),
10401 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10402 uuidRegistryParent);
10403 if (FAILED(rc)) throw rc;
10404
10405 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10406 * the push_back? Looks like we're going to release medium with the
10407 * wrong kind of lock (general issue with if we fail anywhere at all)
10408 * and an orphaned VDI in the snapshots folder. */
10409
10410 /* update the appropriate lock list */
10411 MediumLockList *pMediumLockList;
10412 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10413 AssertComRCThrowRC(rc);
10414 if (aOnline)
10415 {
10416 alock.release();
10417 /* The currently attached medium will be read-only, change
10418 * the lock type to read. */
10419 rc = pMediumLockList->Update(pMedium, false);
10420 alock.acquire();
10421 AssertComRCThrowRC(rc);
10422 }
10423
10424 /* release the locks before the potentially lengthy operation */
10425 alock.release();
10426 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10427 pMediumLockList,
10428 NULL /* aProgress */,
10429 true /* aWait */);
10430 alock.acquire();
10431 if (FAILED(rc)) throw rc;
10432
10433 rc = lockedMediaMap->Unlock();
10434 AssertComRCThrowRC(rc);
10435 alock.release();
10436 rc = pMediumLockList->Append(diff, true);
10437 alock.acquire();
10438 AssertComRCThrowRC(rc);
10439 alock.release();
10440 rc = lockedMediaMap->Lock();
10441 alock.acquire();
10442 AssertComRCThrowRC(rc);
10443
10444 rc = diff->addBackReference(mData->mUuid);
10445 AssertComRCThrowRC(rc);
10446
10447 /* add a new attachment */
10448 ComObjPtr<MediumAttachment> attachment;
10449 attachment.createObject();
10450 rc = attachment->init(this,
10451 diff,
10452 pAtt->getControllerName(),
10453 pAtt->getPort(),
10454 pAtt->getDevice(),
10455 DeviceType_HardDisk,
10456 true /* aImplicit */,
10457 false /* aPassthrough */,
10458 false /* aTempEject */,
10459 pAtt->getNonRotational(),
10460 pAtt->getDiscard(),
10461 pAtt->getBandwidthGroup());
10462 if (FAILED(rc)) throw rc;
10463
10464 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10465 AssertComRCThrowRC(rc);
10466 mMediaData->mAttachments.push_back(attachment);
10467 }
10468 }
10469 catch (HRESULT aRC) { rc = aRC; }
10470
10471 /* unlock all hard disks we locked when there is no VM */
10472 if (!aOnline)
10473 {
10474 ErrorInfoKeeper eik;
10475
10476 HRESULT rc1 = lockedMediaMap->Clear();
10477 AssertComRC(rc1);
10478 }
10479
10480 return rc;
10481}
10482
10483/**
10484 * Deletes implicit differencing hard disks created either by
10485 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10486 *
10487 * Note that to delete hard disks created by #AttachDevice() this method is
10488 * called from #fixupMedia() when the changes are rolled back.
10489 *
10490 * @note Locks this object and the media tree for writing.
10491 */
10492HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10493{
10494 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10495
10496 AutoCaller autoCaller(this);
10497 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10498
10499 AutoMultiWriteLock2 alock(this->lockHandle(),
10500 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10501
10502 /* We absolutely must have backed up state. */
10503 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10504
10505 /* Check if there are any implicitly created diff images. */
10506 bool fImplicitDiffs = false;
10507 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10508 it != mMediaData->mAttachments.end();
10509 ++it)
10510 {
10511 const ComObjPtr<MediumAttachment> &pAtt = *it;
10512 if (pAtt->isImplicit())
10513 {
10514 fImplicitDiffs = true;
10515 break;
10516 }
10517 }
10518 /* If there is nothing to do, leave early. This saves lots of image locking
10519 * effort. It also avoids a MachineStateChanged event without real reason.
10520 * This is important e.g. when loading a VM config, because there should be
10521 * no events. Otherwise API clients can become thoroughly confused for
10522 * inaccessible VMs (the code for loading VM configs uses this method for
10523 * cleanup if the config makes no sense), as they take such events as an
10524 * indication that the VM is alive, and they would force the VM config to
10525 * be reread, leading to an endless loop. */
10526 if (!fImplicitDiffs)
10527 return S_OK;
10528
10529 HRESULT rc = S_OK;
10530 MachineState_T oldState = mData->mMachineState;
10531
10532 /* will release the lock before the potentially lengthy operation,
10533 * so protect with the special state (unless already protected) */
10534 if ( oldState != MachineState_Saving
10535 && oldState != MachineState_LiveSnapshotting
10536 && oldState != MachineState_RestoringSnapshot
10537 && oldState != MachineState_DeletingSnapshot
10538 && oldState != MachineState_DeletingSnapshotOnline
10539 && oldState != MachineState_DeletingSnapshotPaused
10540 )
10541 setMachineState(MachineState_SettingUp);
10542
10543 // use appropriate locked media map (online or offline)
10544 MediumLockListMap lockedMediaOffline;
10545 MediumLockListMap *lockedMediaMap;
10546 if (aOnline)
10547 lockedMediaMap = &mData->mSession.mLockedMedia;
10548 else
10549 lockedMediaMap = &lockedMediaOffline;
10550
10551 try
10552 {
10553 if (!aOnline)
10554 {
10555 /* lock all attached hard disks early to detect "in use"
10556 * situations before deleting actual diffs */
10557 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10558 it != mMediaData->mAttachments.end();
10559 ++it)
10560 {
10561 MediumAttachment* pAtt = *it;
10562 if (pAtt->getType() == DeviceType_HardDisk)
10563 {
10564 Medium* pMedium = pAtt->getMedium();
10565 Assert(pMedium);
10566
10567 MediumLockList *pMediumLockList(new MediumLockList());
10568 alock.release();
10569 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10570 false /* fMediumLockWrite */,
10571 NULL,
10572 *pMediumLockList);
10573 alock.acquire();
10574
10575 if (FAILED(rc))
10576 {
10577 delete pMediumLockList;
10578 throw rc;
10579 }
10580
10581 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10582 if (FAILED(rc))
10583 throw rc;
10584 }
10585 }
10586
10587 if (FAILED(rc))
10588 throw rc;
10589 } // end of offline
10590
10591 /* Lock lists are now up to date and include implicitly created media */
10592
10593 /* Go through remembered attachments and delete all implicitly created
10594 * diffs and fix up the attachment information */
10595 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10596 MediaData::AttachmentList implicitAtts;
10597 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10598 it != mMediaData->mAttachments.end();
10599 ++it)
10600 {
10601 ComObjPtr<MediumAttachment> pAtt = *it;
10602 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10603 if (pMedium.isNull())
10604 continue;
10605
10606 // Implicit attachments go on the list for deletion and back references are removed.
10607 if (pAtt->isImplicit())
10608 {
10609 /* Deassociate and mark for deletion */
10610 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10611 rc = pMedium->removeBackReference(mData->mUuid);
10612 if (FAILED(rc))
10613 throw rc;
10614 implicitAtts.push_back(pAtt);
10615 continue;
10616 }
10617
10618 /* Was this medium attached before? */
10619 if (!findAttachment(oldAtts, pMedium))
10620 {
10621 /* no: de-associate */
10622 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10623 rc = pMedium->removeBackReference(mData->mUuid);
10624 if (FAILED(rc))
10625 throw rc;
10626 continue;
10627 }
10628 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10629 }
10630
10631 /* If there are implicit attachments to delete, throw away the lock
10632 * map contents (which will unlock all media) since the medium
10633 * attachments will be rolled back. Below we need to completely
10634 * recreate the lock map anyway since it is infinitely complex to
10635 * do this incrementally (would need reconstructing each attachment
10636 * change, which would be extremely hairy). */
10637 if (implicitAtts.size() != 0)
10638 {
10639 ErrorInfoKeeper eik;
10640
10641 HRESULT rc1 = lockedMediaMap->Clear();
10642 AssertComRC(rc1);
10643 }
10644
10645 /* rollback hard disk changes */
10646 mMediaData.rollback();
10647
10648 MultiResult mrc(S_OK);
10649
10650 // Delete unused implicit diffs.
10651 if (implicitAtts.size() != 0)
10652 {
10653 alock.release();
10654
10655 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10656 it != implicitAtts.end();
10657 ++it)
10658 {
10659 // Remove medium associated with this attachment.
10660 ComObjPtr<MediumAttachment> pAtt = *it;
10661 Assert(pAtt);
10662 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10663 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10664 Assert(pMedium);
10665
10666 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10667 // continue on delete failure, just collect error messages
10668 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10669 mrc = rc;
10670 }
10671
10672 alock.acquire();
10673
10674 /* if there is a VM recreate media lock map as mentioned above,
10675 * otherwise it is a waste of time and we leave things unlocked */
10676 if (aOnline)
10677 {
10678 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10679 /* must never be NULL, but better safe than sorry */
10680 if (!pMachine.isNull())
10681 {
10682 alock.release();
10683 rc = mData->mSession.mMachine->lockMedia();
10684 alock.acquire();
10685 if (FAILED(rc))
10686 throw rc;
10687 }
10688 }
10689 }
10690 }
10691 catch (HRESULT aRC) {rc = aRC;}
10692
10693 if (mData->mMachineState == MachineState_SettingUp)
10694 setMachineState(oldState);
10695
10696 /* unlock all hard disks we locked when there is no VM */
10697 if (!aOnline)
10698 {
10699 ErrorInfoKeeper eik;
10700
10701 HRESULT rc1 = lockedMediaMap->Clear();
10702 AssertComRC(rc1);
10703 }
10704
10705 return rc;
10706}
10707
10708
10709/**
10710 * Looks through the given list of media attachments for one with the given parameters
10711 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10712 * can be searched as well if needed.
10713 *
10714 * @param list
10715 * @param aControllerName
10716 * @param aControllerPort
10717 * @param aDevice
10718 * @return
10719 */
10720MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10721 IN_BSTR aControllerName,
10722 LONG aControllerPort,
10723 LONG aDevice)
10724{
10725 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10726 it != ll.end();
10727 ++it)
10728 {
10729 MediumAttachment *pAttach = *it;
10730 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10731 return pAttach;
10732 }
10733
10734 return NULL;
10735}
10736
10737/**
10738 * Looks through the given list of media attachments for one with the given parameters
10739 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10740 * can be searched as well if needed.
10741 *
10742 * @param list
10743 * @param aControllerName
10744 * @param aControllerPort
10745 * @param aDevice
10746 * @return
10747 */
10748MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10749 ComObjPtr<Medium> pMedium)
10750{
10751 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10752 it != ll.end();
10753 ++it)
10754 {
10755 MediumAttachment *pAttach = *it;
10756 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10757 if (pMediumThis == pMedium)
10758 return pAttach;
10759 }
10760
10761 return NULL;
10762}
10763
10764/**
10765 * Looks through the given list of media attachments for one with the given parameters
10766 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10767 * can be searched as well if needed.
10768 *
10769 * @param list
10770 * @param aControllerName
10771 * @param aControllerPort
10772 * @param aDevice
10773 * @return
10774 */
10775MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10776 Guid &id)
10777{
10778 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10779 it != ll.end();
10780 ++it)
10781 {
10782 MediumAttachment *pAttach = *it;
10783 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10784 if (pMediumThis->getId() == id)
10785 return pAttach;
10786 }
10787
10788 return NULL;
10789}
10790
10791/**
10792 * Main implementation for Machine::DetachDevice. This also gets called
10793 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10794 *
10795 * @param pAttach Medium attachment to detach.
10796 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10797 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10798 * @return
10799 */
10800HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10801 AutoWriteLock &writeLock,
10802 Snapshot *pSnapshot)
10803{
10804 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10805 DeviceType_T mediumType = pAttach->getType();
10806
10807 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10808
10809 if (pAttach->isImplicit())
10810 {
10811 /* attempt to implicitly delete the implicitly created diff */
10812
10813 /// @todo move the implicit flag from MediumAttachment to Medium
10814 /// and forbid any hard disk operation when it is implicit. Or maybe
10815 /// a special media state for it to make it even more simple.
10816
10817 Assert(mMediaData.isBackedUp());
10818
10819 /* will release the lock before the potentially lengthy operation, so
10820 * protect with the special state */
10821 MachineState_T oldState = mData->mMachineState;
10822 setMachineState(MachineState_SettingUp);
10823
10824 writeLock.release();
10825
10826 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10827 true /*aWait*/);
10828
10829 writeLock.acquire();
10830
10831 setMachineState(oldState);
10832
10833 if (FAILED(rc)) return rc;
10834 }
10835
10836 setModified(IsModified_Storage);
10837 mMediaData.backup();
10838 mMediaData->mAttachments.remove(pAttach);
10839
10840 if (!oldmedium.isNull())
10841 {
10842 // if this is from a snapshot, do not defer detachment to commitMedia()
10843 if (pSnapshot)
10844 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10845 // else if non-hard disk media, do not defer detachment to commitMedia() either
10846 else if (mediumType != DeviceType_HardDisk)
10847 oldmedium->removeBackReference(mData->mUuid);
10848 }
10849
10850 return S_OK;
10851}
10852
10853/**
10854 * Goes thru all media of the given list and
10855 *
10856 * 1) calls detachDevice() on each of them for this machine and
10857 * 2) adds all Medium objects found in the process to the given list,
10858 * depending on cleanupMode.
10859 *
10860 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10861 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10862 * media to the list.
10863 *
10864 * This gets called from Machine::Unregister, both for the actual Machine and
10865 * the SnapshotMachine objects that might be found in the snapshots.
10866 *
10867 * Requires caller and locking. The machine lock must be passed in because it
10868 * will be passed on to detachDevice which needs it for temporary unlocking.
10869 *
10870 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10871 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10872 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10873 * otherwise no media get added.
10874 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10875 * @return
10876 */
10877HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10878 Snapshot *pSnapshot,
10879 CleanupMode_T cleanupMode,
10880 MediaList &llMedia)
10881{
10882 Assert(isWriteLockOnCurrentThread());
10883
10884 HRESULT rc;
10885
10886 // make a temporary list because detachDevice invalidates iterators into
10887 // mMediaData->mAttachments
10888 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10889
10890 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10891 it != llAttachments2.end();
10892 ++it)
10893 {
10894 ComObjPtr<MediumAttachment> &pAttach = *it;
10895 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10896
10897 if (!pMedium.isNull())
10898 {
10899 AutoCaller mac(pMedium);
10900 if (FAILED(mac.rc())) return mac.rc();
10901 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10902 DeviceType_T devType = pMedium->getDeviceType();
10903 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10904 && devType == DeviceType_HardDisk)
10905 || (cleanupMode == CleanupMode_Full)
10906 )
10907 {
10908 llMedia.push_back(pMedium);
10909 ComObjPtr<Medium> pParent = pMedium->getParent();
10910 /*
10911 * Search for medias which are not attached to any machine, but
10912 * in the chain to an attached disk. Mediums are only consided
10913 * if they are:
10914 * - have only one child
10915 * - no references to any machines
10916 * - are of normal medium type
10917 */
10918 while (!pParent.isNull())
10919 {
10920 AutoCaller mac1(pParent);
10921 if (FAILED(mac1.rc())) return mac1.rc();
10922 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10923 if (pParent->getChildren().size() == 1)
10924 {
10925 if ( pParent->getMachineBackRefCount() == 0
10926 && pParent->getType() == MediumType_Normal
10927 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10928 llMedia.push_back(pParent);
10929 }
10930 else
10931 break;
10932 pParent = pParent->getParent();
10933 }
10934 }
10935 }
10936
10937 // real machine: then we need to use the proper method
10938 rc = detachDevice(pAttach, writeLock, pSnapshot);
10939
10940 if (FAILED(rc))
10941 return rc;
10942 }
10943
10944 return S_OK;
10945}
10946
10947/**
10948 * Perform deferred hard disk detachments.
10949 *
10950 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10951 * backed up).
10952 *
10953 * If @a aOnline is @c true then this method will also unlock the old hard disks
10954 * for which the new implicit diffs were created and will lock these new diffs for
10955 * writing.
10956 *
10957 * @param aOnline Whether the VM was online prior to this operation.
10958 *
10959 * @note Locks this object for writing!
10960 */
10961void Machine::commitMedia(bool aOnline /*= false*/)
10962{
10963 AutoCaller autoCaller(this);
10964 AssertComRCReturnVoid(autoCaller.rc());
10965
10966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10967
10968 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10969
10970 HRESULT rc = S_OK;
10971
10972 /* no attach/detach operations -- nothing to do */
10973 if (!mMediaData.isBackedUp())
10974 return;
10975
10976 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10977 bool fMediaNeedsLocking = false;
10978
10979 /* enumerate new attachments */
10980 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10981 it != mMediaData->mAttachments.end();
10982 ++it)
10983 {
10984 MediumAttachment *pAttach = *it;
10985
10986 pAttach->commit();
10987
10988 Medium* pMedium = pAttach->getMedium();
10989 bool fImplicit = pAttach->isImplicit();
10990
10991 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10992 (pMedium) ? pMedium->getName().c_str() : "NULL",
10993 fImplicit));
10994
10995 /** @todo convert all this Machine-based voodoo to MediumAttachment
10996 * based commit logic. */
10997 if (fImplicit)
10998 {
10999 /* convert implicit attachment to normal */
11000 pAttach->setImplicit(false);
11001
11002 if ( aOnline
11003 && pMedium
11004 && pAttach->getType() == DeviceType_HardDisk
11005 )
11006 {
11007 ComObjPtr<Medium> parent = pMedium->getParent();
11008 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11009
11010 /* update the appropriate lock list */
11011 MediumLockList *pMediumLockList;
11012 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11013 AssertComRC(rc);
11014 if (pMediumLockList)
11015 {
11016 /* unlock if there's a need to change the locking */
11017 if (!fMediaNeedsLocking)
11018 {
11019 rc = mData->mSession.mLockedMedia.Unlock();
11020 AssertComRC(rc);
11021 fMediaNeedsLocking = true;
11022 }
11023 rc = pMediumLockList->Update(parent, false);
11024 AssertComRC(rc);
11025 rc = pMediumLockList->Append(pMedium, true);
11026 AssertComRC(rc);
11027 }
11028 }
11029
11030 continue;
11031 }
11032
11033 if (pMedium)
11034 {
11035 /* was this medium attached before? */
11036 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11037 oldIt != oldAtts.end();
11038 ++oldIt)
11039 {
11040 MediumAttachment *pOldAttach = *oldIt;
11041 if (pOldAttach->getMedium() == pMedium)
11042 {
11043 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11044
11045 /* yes: remove from old to avoid de-association */
11046 oldAtts.erase(oldIt);
11047 break;
11048 }
11049 }
11050 }
11051 }
11052
11053 /* enumerate remaining old attachments and de-associate from the
11054 * current machine state */
11055 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11056 it != oldAtts.end();
11057 ++it)
11058 {
11059 MediumAttachment *pAttach = *it;
11060 Medium* pMedium = pAttach->getMedium();
11061
11062 /* Detach only hard disks, since DVD/floppy media is detached
11063 * instantly in MountMedium. */
11064 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11065 {
11066 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11067
11068 /* now de-associate from the current machine state */
11069 rc = pMedium->removeBackReference(mData->mUuid);
11070 AssertComRC(rc);
11071
11072 if (aOnline)
11073 {
11074 /* unlock since medium is not used anymore */
11075 MediumLockList *pMediumLockList;
11076 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11077 AssertComRC(rc);
11078 if (pMediumLockList)
11079 {
11080 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11081 AssertComRC(rc);
11082 }
11083 }
11084 }
11085 }
11086
11087 /* take media locks again so that the locking state is consistent */
11088 if (fMediaNeedsLocking)
11089 {
11090 Assert(aOnline);
11091 rc = mData->mSession.mLockedMedia.Lock();
11092 AssertComRC(rc);
11093 }
11094
11095 /* commit the hard disk changes */
11096 mMediaData.commit();
11097
11098 if (isSessionMachine())
11099 {
11100 /*
11101 * Update the parent machine to point to the new owner.
11102 * This is necessary because the stored parent will point to the
11103 * session machine otherwise and cause crashes or errors later
11104 * when the session machine gets invalid.
11105 */
11106 /** @todo Change the MediumAttachment class to behave like any other
11107 * class in this regard by creating peer MediumAttachment
11108 * objects for session machines and share the data with the peer
11109 * machine.
11110 */
11111 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11112 it != mMediaData->mAttachments.end();
11113 ++it)
11114 {
11115 (*it)->updateParentMachine(mPeer);
11116 }
11117
11118 /* attach new data to the primary machine and reshare it */
11119 mPeer->mMediaData.attach(mMediaData);
11120 }
11121
11122 return;
11123}
11124
11125/**
11126 * Perform deferred deletion of implicitly created diffs.
11127 *
11128 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11129 * backed up).
11130 *
11131 * @note Locks this object for writing!
11132 */
11133void Machine::rollbackMedia()
11134{
11135 AutoCaller autoCaller(this);
11136 AssertComRCReturnVoid(autoCaller.rc());
11137
11138 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11139 LogFlowThisFunc(("Entering rollbackMedia\n"));
11140
11141 HRESULT rc = S_OK;
11142
11143 /* no attach/detach operations -- nothing to do */
11144 if (!mMediaData.isBackedUp())
11145 return;
11146
11147 /* enumerate new attachments */
11148 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11149 it != mMediaData->mAttachments.end();
11150 ++it)
11151 {
11152 MediumAttachment *pAttach = *it;
11153 /* Fix up the backrefs for DVD/floppy media. */
11154 if (pAttach->getType() != DeviceType_HardDisk)
11155 {
11156 Medium* pMedium = pAttach->getMedium();
11157 if (pMedium)
11158 {
11159 rc = pMedium->removeBackReference(mData->mUuid);
11160 AssertComRC(rc);
11161 }
11162 }
11163
11164 (*it)->rollback();
11165
11166 pAttach = *it;
11167 /* Fix up the backrefs for DVD/floppy media. */
11168 if (pAttach->getType() != DeviceType_HardDisk)
11169 {
11170 Medium* pMedium = pAttach->getMedium();
11171 if (pMedium)
11172 {
11173 rc = pMedium->addBackReference(mData->mUuid);
11174 AssertComRC(rc);
11175 }
11176 }
11177 }
11178
11179 /** @todo convert all this Machine-based voodoo to MediumAttachment
11180 * based rollback logic. */
11181 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11182
11183 return;
11184}
11185
11186/**
11187 * Returns true if the settings file is located in the directory named exactly
11188 * as the machine; this means, among other things, that the machine directory
11189 * should be auto-renamed.
11190 *
11191 * @param aSettingsDir if not NULL, the full machine settings file directory
11192 * name will be assigned there.
11193 *
11194 * @note Doesn't lock anything.
11195 * @note Not thread safe (must be called from this object's lock).
11196 */
11197bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11198{
11199 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11200 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11201 if (aSettingsDir)
11202 *aSettingsDir = strMachineDirName;
11203 strMachineDirName.stripPath(); // vmname
11204 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11205 strConfigFileOnly.stripPath() // vmname.vbox
11206 .stripExt(); // vmname
11207 /** @todo hack, make somehow use of ComposeMachineFilename */
11208 if (mUserData->s.fDirectoryIncludesUUID)
11209 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11210
11211 AssertReturn(!strMachineDirName.isEmpty(), false);
11212 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11213
11214 return strMachineDirName == strConfigFileOnly;
11215}
11216
11217/**
11218 * Discards all changes to machine settings.
11219 *
11220 * @param aNotify Whether to notify the direct session about changes or not.
11221 *
11222 * @note Locks objects for writing!
11223 */
11224void Machine::rollback(bool aNotify)
11225{
11226 AutoCaller autoCaller(this);
11227 AssertComRCReturn(autoCaller.rc(), (void)0);
11228
11229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11230
11231 if (!mStorageControllers.isNull())
11232 {
11233 if (mStorageControllers.isBackedUp())
11234 {
11235 /* unitialize all new devices (absent in the backed up list). */
11236 StorageControllerList::const_iterator it = mStorageControllers->begin();
11237 StorageControllerList *backedList = mStorageControllers.backedUpData();
11238 while (it != mStorageControllers->end())
11239 {
11240 if ( std::find(backedList->begin(), backedList->end(), *it)
11241 == backedList->end()
11242 )
11243 {
11244 (*it)->uninit();
11245 }
11246 ++it;
11247 }
11248
11249 /* restore the list */
11250 mStorageControllers.rollback();
11251 }
11252
11253 /* rollback any changes to devices after restoring the list */
11254 if (mData->flModifications & IsModified_Storage)
11255 {
11256 StorageControllerList::const_iterator it = mStorageControllers->begin();
11257 while (it != mStorageControllers->end())
11258 {
11259 (*it)->rollback();
11260 ++it;
11261 }
11262 }
11263 }
11264
11265 mUserData.rollback();
11266
11267 mHWData.rollback();
11268
11269 if (mData->flModifications & IsModified_Storage)
11270 rollbackMedia();
11271
11272 if (mBIOSSettings)
11273 mBIOSSettings->rollback();
11274
11275 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11276 mVRDEServer->rollback();
11277
11278 if (mAudioAdapter)
11279 mAudioAdapter->rollback();
11280
11281 if (mUSBController && (mData->flModifications & IsModified_USB))
11282 mUSBController->rollback();
11283
11284 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11285 mBandwidthControl->rollback();
11286
11287 if (!mHWData.isNull())
11288 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11289 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11290 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11291 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11292
11293 if (mData->flModifications & IsModified_NetworkAdapters)
11294 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11295 if ( mNetworkAdapters[slot]
11296 && mNetworkAdapters[slot]->isModified())
11297 {
11298 mNetworkAdapters[slot]->rollback();
11299 networkAdapters[slot] = mNetworkAdapters[slot];
11300 }
11301
11302 if (mData->flModifications & IsModified_SerialPorts)
11303 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11304 if ( mSerialPorts[slot]
11305 && mSerialPorts[slot]->isModified())
11306 {
11307 mSerialPorts[slot]->rollback();
11308 serialPorts[slot] = mSerialPorts[slot];
11309 }
11310
11311 if (mData->flModifications & IsModified_ParallelPorts)
11312 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11313 if ( mParallelPorts[slot]
11314 && mParallelPorts[slot]->isModified())
11315 {
11316 mParallelPorts[slot]->rollback();
11317 parallelPorts[slot] = mParallelPorts[slot];
11318 }
11319
11320 if (aNotify)
11321 {
11322 /* inform the direct session about changes */
11323
11324 ComObjPtr<Machine> that = this;
11325 uint32_t flModifications = mData->flModifications;
11326 alock.release();
11327
11328 if (flModifications & IsModified_SharedFolders)
11329 that->onSharedFolderChange();
11330
11331 if (flModifications & IsModified_VRDEServer)
11332 that->onVRDEServerChange(/* aRestart */ TRUE);
11333 if (flModifications & IsModified_USB)
11334 that->onUSBControllerChange();
11335
11336 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11337 if (networkAdapters[slot])
11338 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11339 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11340 if (serialPorts[slot])
11341 that->onSerialPortChange(serialPorts[slot]);
11342 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11343 if (parallelPorts[slot])
11344 that->onParallelPortChange(parallelPorts[slot]);
11345
11346 if (flModifications & IsModified_Storage)
11347 that->onStorageControllerChange();
11348
11349#if 0
11350 if (flModifications & IsModified_BandwidthControl)
11351 that->onBandwidthControlChange();
11352#endif
11353 }
11354}
11355
11356/**
11357 * Commits all the changes to machine settings.
11358 *
11359 * Note that this operation is supposed to never fail.
11360 *
11361 * @note Locks this object and children for writing.
11362 */
11363void Machine::commit()
11364{
11365 AutoCaller autoCaller(this);
11366 AssertComRCReturnVoid(autoCaller.rc());
11367
11368 AutoCaller peerCaller(mPeer);
11369 AssertComRCReturnVoid(peerCaller.rc());
11370
11371 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11372
11373 /*
11374 * use safe commit to ensure Snapshot machines (that share mUserData)
11375 * will still refer to a valid memory location
11376 */
11377 mUserData.commitCopy();
11378
11379 mHWData.commit();
11380
11381 if (mMediaData.isBackedUp())
11382 commitMedia();
11383
11384 mBIOSSettings->commit();
11385 mVRDEServer->commit();
11386 mAudioAdapter->commit();
11387 mUSBController->commit();
11388 mBandwidthControl->commit();
11389
11390 /* Since mNetworkAdapters is a list which might have been changed (resized)
11391 * without using the Backupable<> template we need to handle the copying
11392 * of the list entries manually, including the creation of peers for the
11393 * new objects. */
11394 bool commitNetworkAdapters = false;
11395 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11396 if (mPeer)
11397 {
11398 /* commit everything, even the ones which will go away */
11399 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11400 mNetworkAdapters[slot]->commit();
11401 /* copy over the new entries, creating a peer and uninit the original */
11402 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11403 for (size_t slot = 0; slot < newSize; slot++)
11404 {
11405 /* look if this adapter has a peer device */
11406 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11407 if (!peer)
11408 {
11409 /* no peer means the adapter is a newly created one;
11410 * create a peer owning data this data share it with */
11411 peer.createObject();
11412 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11413 }
11414 mPeer->mNetworkAdapters[slot] = peer;
11415 }
11416 /* uninit any no longer needed network adapters */
11417 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11418 mNetworkAdapters[slot]->uninit();
11419 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11420 {
11421 if (mPeer->mNetworkAdapters[slot])
11422 mPeer->mNetworkAdapters[slot]->uninit();
11423 }
11424 /* Keep the original network adapter count until this point, so that
11425 * discarding a chipset type change will not lose settings. */
11426 mNetworkAdapters.resize(newSize);
11427 mPeer->mNetworkAdapters.resize(newSize);
11428 }
11429 else
11430 {
11431 /* we have no peer (our parent is the newly created machine);
11432 * just commit changes to the network adapters */
11433 commitNetworkAdapters = true;
11434 }
11435 if (commitNetworkAdapters)
11436 {
11437 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11438 mNetworkAdapters[slot]->commit();
11439 }
11440
11441 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11442 mSerialPorts[slot]->commit();
11443 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11444 mParallelPorts[slot]->commit();
11445
11446 bool commitStorageControllers = false;
11447
11448 if (mStorageControllers.isBackedUp())
11449 {
11450 mStorageControllers.commit();
11451
11452 if (mPeer)
11453 {
11454 /* Commit all changes to new controllers (this will reshare data with
11455 * peers for those who have peers) */
11456 StorageControllerList *newList = new StorageControllerList();
11457 StorageControllerList::const_iterator it = mStorageControllers->begin();
11458 while (it != mStorageControllers->end())
11459 {
11460 (*it)->commit();
11461
11462 /* look if this controller has a peer device */
11463 ComObjPtr<StorageController> peer = (*it)->getPeer();
11464 if (!peer)
11465 {
11466 /* no peer means the device is a newly created one;
11467 * create a peer owning data this device share it with */
11468 peer.createObject();
11469 peer->init(mPeer, *it, true /* aReshare */);
11470 }
11471 else
11472 {
11473 /* remove peer from the old list */
11474 mPeer->mStorageControllers->remove(peer);
11475 }
11476 /* and add it to the new list */
11477 newList->push_back(peer);
11478
11479 ++it;
11480 }
11481
11482 /* uninit old peer's controllers that are left */
11483 it = mPeer->mStorageControllers->begin();
11484 while (it != mPeer->mStorageControllers->end())
11485 {
11486 (*it)->uninit();
11487 ++it;
11488 }
11489
11490 /* attach new list of controllers to our peer */
11491 mPeer->mStorageControllers.attach(newList);
11492 }
11493 else
11494 {
11495 /* we have no peer (our parent is the newly created machine);
11496 * just commit changes to devices */
11497 commitStorageControllers = true;
11498 }
11499 }
11500 else
11501 {
11502 /* the list of controllers itself is not changed,
11503 * just commit changes to controllers themselves */
11504 commitStorageControllers = true;
11505 }
11506
11507 if (commitStorageControllers)
11508 {
11509 StorageControllerList::const_iterator it = mStorageControllers->begin();
11510 while (it != mStorageControllers->end())
11511 {
11512 (*it)->commit();
11513 ++it;
11514 }
11515 }
11516
11517 if (isSessionMachine())
11518 {
11519 /* attach new data to the primary machine and reshare it */
11520 mPeer->mUserData.attach(mUserData);
11521 mPeer->mHWData.attach(mHWData);
11522 /* mMediaData is reshared by fixupMedia */
11523 // mPeer->mMediaData.attach(mMediaData);
11524 Assert(mPeer->mMediaData.data() == mMediaData.data());
11525 }
11526}
11527
11528/**
11529 * Copies all the hardware data from the given machine.
11530 *
11531 * Currently, only called when the VM is being restored from a snapshot. In
11532 * particular, this implies that the VM is not running during this method's
11533 * call.
11534 *
11535 * @note This method must be called from under this object's lock.
11536 *
11537 * @note This method doesn't call #commit(), so all data remains backed up and
11538 * unsaved.
11539 */
11540void Machine::copyFrom(Machine *aThat)
11541{
11542 AssertReturnVoid(!isSnapshotMachine());
11543 AssertReturnVoid(aThat->isSnapshotMachine());
11544
11545 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11546
11547 mHWData.assignCopy(aThat->mHWData);
11548
11549 // create copies of all shared folders (mHWData after attaching a copy
11550 // contains just references to original objects)
11551 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11552 it != mHWData->mSharedFolders.end();
11553 ++it)
11554 {
11555 ComObjPtr<SharedFolder> folder;
11556 folder.createObject();
11557 HRESULT rc = folder->initCopy(getMachine(), *it);
11558 AssertComRC(rc);
11559 *it = folder;
11560 }
11561
11562 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11563 mVRDEServer->copyFrom(aThat->mVRDEServer);
11564 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11565 mUSBController->copyFrom(aThat->mUSBController);
11566 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11567
11568 /* create private copies of all controllers */
11569 mStorageControllers.backup();
11570 mStorageControllers->clear();
11571 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11572 it != aThat->mStorageControllers->end();
11573 ++it)
11574 {
11575 ComObjPtr<StorageController> ctrl;
11576 ctrl.createObject();
11577 ctrl->initCopy(this, *it);
11578 mStorageControllers->push_back(ctrl);
11579 }
11580
11581 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11582 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11583 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11584 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11585 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11586 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11587 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11588}
11589
11590/**
11591 * Returns whether the given storage controller is hotplug capable.
11592 *
11593 * @returns true if the controller supports hotplugging
11594 * false otherwise.
11595 * @param enmCtrlType The controller type to check for.
11596 */
11597bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11598{
11599 switch (enmCtrlType)
11600 {
11601 case StorageControllerType_IntelAhci:
11602 return true;
11603 case StorageControllerType_LsiLogic:
11604 case StorageControllerType_LsiLogicSas:
11605 case StorageControllerType_BusLogic:
11606 case StorageControllerType_PIIX3:
11607 case StorageControllerType_PIIX4:
11608 case StorageControllerType_ICH6:
11609 case StorageControllerType_I82078:
11610 default:
11611 return false;
11612 }
11613}
11614
11615#ifdef VBOX_WITH_RESOURCE_USAGE_API
11616
11617void Machine::getDiskList(MediaList &list)
11618{
11619 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11620 it != mMediaData->mAttachments.end();
11621 ++it)
11622 {
11623 MediumAttachment* pAttach = *it;
11624 /* just in case */
11625 AssertStmt(pAttach, continue);
11626
11627 AutoCaller localAutoCallerA(pAttach);
11628 if (FAILED(localAutoCallerA.rc())) continue;
11629
11630 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11631
11632 if (pAttach->getType() == DeviceType_HardDisk)
11633 list.push_back(pAttach->getMedium());
11634 }
11635}
11636
11637void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11638{
11639 AssertReturnVoid(isWriteLockOnCurrentThread());
11640 AssertPtrReturnVoid(aCollector);
11641
11642 pm::CollectorHAL *hal = aCollector->getHAL();
11643 /* Create sub metrics */
11644 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11645 "Percentage of processor time spent in user mode by the VM process.");
11646 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11647 "Percentage of processor time spent in kernel mode by the VM process.");
11648 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11649 "Size of resident portion of VM process in memory.");
11650 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11651 "Actual size of all VM disks combined.");
11652 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11653 "Network receive rate.");
11654 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11655 "Network transmit rate.");
11656 /* Create and register base metrics */
11657 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11658 cpuLoadUser, cpuLoadKernel);
11659 aCollector->registerBaseMetric(cpuLoad);
11660 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11661 ramUsageUsed);
11662 aCollector->registerBaseMetric(ramUsage);
11663 MediaList disks;
11664 getDiskList(disks);
11665 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11666 diskUsageUsed);
11667 aCollector->registerBaseMetric(diskUsage);
11668
11669 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11670 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11671 new pm::AggregateAvg()));
11672 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11673 new pm::AggregateMin()));
11674 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11675 new pm::AggregateMax()));
11676 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11677 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11678 new pm::AggregateAvg()));
11679 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11680 new pm::AggregateMin()));
11681 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11682 new pm::AggregateMax()));
11683
11684 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11685 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11686 new pm::AggregateAvg()));
11687 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11688 new pm::AggregateMin()));
11689 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11690 new pm::AggregateMax()));
11691
11692 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11693 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11694 new pm::AggregateAvg()));
11695 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11696 new pm::AggregateMin()));
11697 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11698 new pm::AggregateMax()));
11699
11700
11701 /* Guest metrics collector */
11702 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11703 aCollector->registerGuest(mCollectorGuest);
11704 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11705 this, __PRETTY_FUNCTION__, mCollectorGuest));
11706
11707 /* Create sub metrics */
11708 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11709 "Percentage of processor time spent in user mode as seen by the guest.");
11710 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11711 "Percentage of processor time spent in kernel mode as seen by the guest.");
11712 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11713 "Percentage of processor time spent idling as seen by the guest.");
11714
11715 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11716 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11717 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11718 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11719 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11720 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11721
11722 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11723
11724 /* Create and register base metrics */
11725 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11726 machineNetRx, machineNetTx);
11727 aCollector->registerBaseMetric(machineNetRate);
11728
11729 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11730 guestLoadUser, guestLoadKernel, guestLoadIdle);
11731 aCollector->registerBaseMetric(guestCpuLoad);
11732
11733 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11734 guestMemTotal, guestMemFree,
11735 guestMemBalloon, guestMemShared,
11736 guestMemCache, guestPagedTotal);
11737 aCollector->registerBaseMetric(guestCpuMem);
11738
11739 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11740 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11741 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11742 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11743
11744 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11745 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11746 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11747 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11748
11749 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11750 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11751 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11752 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11753
11754 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11755 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11756 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11757 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11758
11759 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11760 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11761 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11762 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11763
11764 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11765 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11766 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11767 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11768
11769 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11770 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11771 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11772 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11773
11774 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11775 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11776 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11777 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11778
11779 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11780 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11781 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11782 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11783
11784 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11785 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11786 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11787 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11788
11789 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11790 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11791 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11792 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11793}
11794
11795void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11796{
11797 AssertReturnVoid(isWriteLockOnCurrentThread());
11798
11799 if (aCollector)
11800 {
11801 aCollector->unregisterMetricsFor(aMachine);
11802 aCollector->unregisterBaseMetricsFor(aMachine);
11803 }
11804}
11805
11806#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11807
11808
11809////////////////////////////////////////////////////////////////////////////////
11810
11811DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11812
11813HRESULT SessionMachine::FinalConstruct()
11814{
11815 LogFlowThisFunc(("\n"));
11816
11817#if defined(RT_OS_WINDOWS)
11818 mIPCSem = NULL;
11819#elif defined(RT_OS_OS2)
11820 mIPCSem = NULLHANDLE;
11821#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11822 mIPCSem = -1;
11823#else
11824# error "Port me!"
11825#endif
11826
11827 return BaseFinalConstruct();
11828}
11829
11830void SessionMachine::FinalRelease()
11831{
11832 LogFlowThisFunc(("\n"));
11833
11834 uninit(Uninit::Unexpected);
11835
11836 BaseFinalRelease();
11837}
11838
11839/**
11840 * @note Must be called only by Machine::openSession() from its own write lock.
11841 */
11842HRESULT SessionMachine::init(Machine *aMachine)
11843{
11844 LogFlowThisFuncEnter();
11845 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11846
11847 AssertReturn(aMachine, E_INVALIDARG);
11848
11849 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11850
11851 /* Enclose the state transition NotReady->InInit->Ready */
11852 AutoInitSpan autoInitSpan(this);
11853 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11854
11855 /* create the interprocess semaphore */
11856#if defined(RT_OS_WINDOWS)
11857 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11858 for (size_t i = 0; i < mIPCSemName.length(); i++)
11859 if (mIPCSemName.raw()[i] == '\\')
11860 mIPCSemName.raw()[i] = '/';
11861 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11862 ComAssertMsgRet(mIPCSem,
11863 ("Cannot create IPC mutex '%ls', err=%d",
11864 mIPCSemName.raw(), ::GetLastError()),
11865 E_FAIL);
11866#elif defined(RT_OS_OS2)
11867 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11868 aMachine->mData->mUuid.raw());
11869 mIPCSemName = ipcSem;
11870 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11871 ComAssertMsgRet(arc == NO_ERROR,
11872 ("Cannot create IPC mutex '%s', arc=%ld",
11873 ipcSem.c_str(), arc),
11874 E_FAIL);
11875#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11876# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11877# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11878 /** @todo Check that this still works correctly. */
11879 AssertCompileSize(key_t, 8);
11880# else
11881 AssertCompileSize(key_t, 4);
11882# endif
11883 key_t key;
11884 mIPCSem = -1;
11885 mIPCKey = "0";
11886 for (uint32_t i = 0; i < 1 << 24; i++)
11887 {
11888 key = ((uint32_t)'V' << 24) | i;
11889 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11890 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11891 {
11892 mIPCSem = sem;
11893 if (sem >= 0)
11894 mIPCKey = BstrFmt("%u", key);
11895 break;
11896 }
11897 }
11898# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11899 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11900 char *pszSemName = NULL;
11901 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11902 key_t key = ::ftok(pszSemName, 'V');
11903 RTStrFree(pszSemName);
11904
11905 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11906# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11907
11908 int errnoSave = errno;
11909 if (mIPCSem < 0 && errnoSave == ENOSYS)
11910 {
11911 setError(E_FAIL,
11912 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11913 "support for SysV IPC. Check the host kernel configuration for "
11914 "CONFIG_SYSVIPC=y"));
11915 return E_FAIL;
11916 }
11917 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11918 * the IPC semaphores */
11919 if (mIPCSem < 0 && errnoSave == ENOSPC)
11920 {
11921#ifdef RT_OS_LINUX
11922 setError(E_FAIL,
11923 tr("Cannot create IPC semaphore because the system limit for the "
11924 "maximum number of semaphore sets (SEMMNI), or the system wide "
11925 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11926 "current set of SysV IPC semaphores can be determined from "
11927 "the file /proc/sysvipc/sem"));
11928#else
11929 setError(E_FAIL,
11930 tr("Cannot create IPC semaphore because the system-imposed limit "
11931 "on the maximum number of allowed semaphores or semaphore "
11932 "identifiers system-wide would be exceeded"));
11933#endif
11934 return E_FAIL;
11935 }
11936 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11937 E_FAIL);
11938 /* set the initial value to 1 */
11939 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11940 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11941 E_FAIL);
11942#else
11943# error "Port me!"
11944#endif
11945
11946 /* memorize the peer Machine */
11947 unconst(mPeer) = aMachine;
11948 /* share the parent pointer */
11949 unconst(mParent) = aMachine->mParent;
11950
11951 /* take the pointers to data to share */
11952 mData.share(aMachine->mData);
11953 mSSData.share(aMachine->mSSData);
11954
11955 mUserData.share(aMachine->mUserData);
11956 mHWData.share(aMachine->mHWData);
11957 mMediaData.share(aMachine->mMediaData);
11958
11959 mStorageControllers.allocate();
11960 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11961 it != aMachine->mStorageControllers->end();
11962 ++it)
11963 {
11964 ComObjPtr<StorageController> ctl;
11965 ctl.createObject();
11966 ctl->init(this, *it);
11967 mStorageControllers->push_back(ctl);
11968 }
11969
11970 unconst(mBIOSSettings).createObject();
11971 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11972 /* create another VRDEServer object that will be mutable */
11973 unconst(mVRDEServer).createObject();
11974 mVRDEServer->init(this, aMachine->mVRDEServer);
11975 /* create another audio adapter object that will be mutable */
11976 unconst(mAudioAdapter).createObject();
11977 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11978 /* create a list of serial ports that will be mutable */
11979 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11980 {
11981 unconst(mSerialPorts[slot]).createObject();
11982 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11983 }
11984 /* create a list of parallel ports that will be mutable */
11985 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11986 {
11987 unconst(mParallelPorts[slot]).createObject();
11988 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11989 }
11990 /* create another USB controller object that will be mutable */
11991 unconst(mUSBController).createObject();
11992 mUSBController->init(this, aMachine->mUSBController);
11993
11994 /* create a list of network adapters that will be mutable */
11995 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11996 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11997 {
11998 unconst(mNetworkAdapters[slot]).createObject();
11999 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12000 }
12001
12002 /* create another bandwidth control object that will be mutable */
12003 unconst(mBandwidthControl).createObject();
12004 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12005
12006 /* default is to delete saved state on Saved -> PoweredOff transition */
12007 mRemoveSavedState = true;
12008
12009 /* Confirm a successful initialization when it's the case */
12010 autoInitSpan.setSucceeded();
12011
12012 LogFlowThisFuncLeave();
12013 return S_OK;
12014}
12015
12016/**
12017 * Uninitializes this session object. If the reason is other than
12018 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12019 *
12020 * @param aReason uninitialization reason
12021 *
12022 * @note Locks mParent + this object for writing.
12023 */
12024void SessionMachine::uninit(Uninit::Reason aReason)
12025{
12026 LogFlowThisFuncEnter();
12027 LogFlowThisFunc(("reason=%d\n", aReason));
12028
12029 /*
12030 * Strongly reference ourselves to prevent this object deletion after
12031 * mData->mSession.mMachine.setNull() below (which can release the last
12032 * reference and call the destructor). Important: this must be done before
12033 * accessing any members (and before AutoUninitSpan that does it as well).
12034 * This self reference will be released as the very last step on return.
12035 */
12036 ComObjPtr<SessionMachine> selfRef = this;
12037
12038 /* Enclose the state transition Ready->InUninit->NotReady */
12039 AutoUninitSpan autoUninitSpan(this);
12040 if (autoUninitSpan.uninitDone())
12041 {
12042 LogFlowThisFunc(("Already uninitialized\n"));
12043 LogFlowThisFuncLeave();
12044 return;
12045 }
12046
12047 if (autoUninitSpan.initFailed())
12048 {
12049 /* We've been called by init() because it's failed. It's not really
12050 * necessary (nor it's safe) to perform the regular uninit sequence
12051 * below, the following is enough.
12052 */
12053 LogFlowThisFunc(("Initialization failed.\n"));
12054#if defined(RT_OS_WINDOWS)
12055 if (mIPCSem)
12056 ::CloseHandle(mIPCSem);
12057 mIPCSem = NULL;
12058#elif defined(RT_OS_OS2)
12059 if (mIPCSem != NULLHANDLE)
12060 ::DosCloseMutexSem(mIPCSem);
12061 mIPCSem = NULLHANDLE;
12062#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12063 if (mIPCSem >= 0)
12064 ::semctl(mIPCSem, 0, IPC_RMID);
12065 mIPCSem = -1;
12066# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12067 mIPCKey = "0";
12068# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12069#else
12070# error "Port me!"
12071#endif
12072 uninitDataAndChildObjects();
12073 mData.free();
12074 unconst(mParent) = NULL;
12075 unconst(mPeer) = NULL;
12076 LogFlowThisFuncLeave();
12077 return;
12078 }
12079
12080 MachineState_T lastState;
12081 {
12082 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12083 lastState = mData->mMachineState;
12084 }
12085 NOREF(lastState);
12086
12087#ifdef VBOX_WITH_USB
12088 // release all captured USB devices, but do this before requesting the locks below
12089 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12090 {
12091 /* Console::captureUSBDevices() is called in the VM process only after
12092 * setting the machine state to Starting or Restoring.
12093 * Console::detachAllUSBDevices() will be called upon successful
12094 * termination. So, we need to release USB devices only if there was
12095 * an abnormal termination of a running VM.
12096 *
12097 * This is identical to SessionMachine::DetachAllUSBDevices except
12098 * for the aAbnormal argument. */
12099 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12100 AssertComRC(rc);
12101 NOREF(rc);
12102
12103 USBProxyService *service = mParent->host()->usbProxyService();
12104 if (service)
12105 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12106 }
12107#endif /* VBOX_WITH_USB */
12108
12109 // we need to lock this object in uninit() because the lock is shared
12110 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12111 // and others need mParent lock, and USB needs host lock.
12112 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12113
12114#if 0
12115 // Trigger async cleanup tasks, avoid doing things here which are not
12116 // vital to be done immediately and maybe need more locks. This calls
12117 // Machine::unregisterMetrics().
12118 mParent->onMachineUninit(mPeer);
12119#else
12120 /*
12121 * It is safe to call Machine::unregisterMetrics() here because
12122 * PerformanceCollector::samplerCallback no longer accesses guest methods
12123 * holding the lock.
12124 */
12125 unregisterMetrics(mParent->performanceCollector(), mPeer);
12126#endif
12127 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12128 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12129 this, __PRETTY_FUNCTION__, mCollectorGuest));
12130 if (mCollectorGuest)
12131 {
12132 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12133 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12134 mCollectorGuest = NULL;
12135 }
12136
12137 if (aReason == Uninit::Abnormal)
12138 {
12139 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12140 Global::IsOnlineOrTransient(lastState)));
12141
12142 /* reset the state to Aborted */
12143 if (mData->mMachineState != MachineState_Aborted)
12144 setMachineState(MachineState_Aborted);
12145 }
12146
12147 // any machine settings modified?
12148 if (mData->flModifications)
12149 {
12150 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12151 rollback(false /* aNotify */);
12152 }
12153
12154 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12155 || !mConsoleTaskData.mSnapshot);
12156 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12157 {
12158 LogWarningThisFunc(("canceling failed save state request!\n"));
12159 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12160 }
12161 else if (!mConsoleTaskData.mSnapshot.isNull())
12162 {
12163 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12164
12165 /* delete all differencing hard disks created (this will also attach
12166 * their parents back by rolling back mMediaData) */
12167 rollbackMedia();
12168
12169 // delete the saved state file (it might have been already created)
12170 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12171 // think it's still in use
12172 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12173 mConsoleTaskData.mSnapshot->uninit();
12174 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12175 }
12176
12177 if (!mData->mSession.mType.isEmpty())
12178 {
12179 /* mType is not null when this machine's process has been started by
12180 * Machine::LaunchVMProcess(), therefore it is our child. We
12181 * need to queue the PID to reap the process (and avoid zombies on
12182 * Linux). */
12183 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12184 mParent->addProcessToReap(mData->mSession.mPID);
12185 }
12186
12187 mData->mSession.mPID = NIL_RTPROCESS;
12188
12189 if (aReason == Uninit::Unexpected)
12190 {
12191 /* Uninitialization didn't come from #checkForDeath(), so tell the
12192 * client watcher thread to update the set of machines that have open
12193 * sessions. */
12194 mParent->updateClientWatcher();
12195 }
12196
12197 /* uninitialize all remote controls */
12198 if (mData->mSession.mRemoteControls.size())
12199 {
12200 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12201 mData->mSession.mRemoteControls.size()));
12202
12203 Data::Session::RemoteControlList::iterator it =
12204 mData->mSession.mRemoteControls.begin();
12205 while (it != mData->mSession.mRemoteControls.end())
12206 {
12207 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12208 HRESULT rc = (*it)->Uninitialize();
12209 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12210 if (FAILED(rc))
12211 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12212 ++it;
12213 }
12214 mData->mSession.mRemoteControls.clear();
12215 }
12216
12217 /*
12218 * An expected uninitialization can come only from #checkForDeath().
12219 * Otherwise it means that something's gone really wrong (for example,
12220 * the Session implementation has released the VirtualBox reference
12221 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12222 * etc). However, it's also possible, that the client releases the IPC
12223 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12224 * but the VirtualBox release event comes first to the server process.
12225 * This case is practically possible, so we should not assert on an
12226 * unexpected uninit, just log a warning.
12227 */
12228
12229 if ((aReason == Uninit::Unexpected))
12230 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12231
12232 if (aReason != Uninit::Normal)
12233 {
12234 mData->mSession.mDirectControl.setNull();
12235 }
12236 else
12237 {
12238 /* this must be null here (see #OnSessionEnd()) */
12239 Assert(mData->mSession.mDirectControl.isNull());
12240 Assert(mData->mSession.mState == SessionState_Unlocking);
12241 Assert(!mData->mSession.mProgress.isNull());
12242 }
12243 if (mData->mSession.mProgress)
12244 {
12245 if (aReason == Uninit::Normal)
12246 mData->mSession.mProgress->notifyComplete(S_OK);
12247 else
12248 mData->mSession.mProgress->notifyComplete(E_FAIL,
12249 COM_IIDOF(ISession),
12250 getComponentName(),
12251 tr("The VM session was aborted"));
12252 mData->mSession.mProgress.setNull();
12253 }
12254
12255 /* remove the association between the peer machine and this session machine */
12256 Assert( (SessionMachine*)mData->mSession.mMachine == this
12257 || aReason == Uninit::Unexpected);
12258
12259 /* reset the rest of session data */
12260 mData->mSession.mMachine.setNull();
12261 mData->mSession.mState = SessionState_Unlocked;
12262 mData->mSession.mType.setNull();
12263
12264 /* close the interprocess semaphore before leaving the exclusive lock */
12265#if defined(RT_OS_WINDOWS)
12266 if (mIPCSem)
12267 ::CloseHandle(mIPCSem);
12268 mIPCSem = NULL;
12269#elif defined(RT_OS_OS2)
12270 if (mIPCSem != NULLHANDLE)
12271 ::DosCloseMutexSem(mIPCSem);
12272 mIPCSem = NULLHANDLE;
12273#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12274 if (mIPCSem >= 0)
12275 ::semctl(mIPCSem, 0, IPC_RMID);
12276 mIPCSem = -1;
12277# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12278 mIPCKey = "0";
12279# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12280#else
12281# error "Port me!"
12282#endif
12283
12284 /* fire an event */
12285 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12286
12287 uninitDataAndChildObjects();
12288
12289 /* free the essential data structure last */
12290 mData.free();
12291
12292 /* release the exclusive lock before setting the below two to NULL */
12293 multilock.release();
12294
12295 unconst(mParent) = NULL;
12296 unconst(mPeer) = NULL;
12297
12298 LogFlowThisFuncLeave();
12299}
12300
12301// util::Lockable interface
12302////////////////////////////////////////////////////////////////////////////////
12303
12304/**
12305 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12306 * with the primary Machine instance (mPeer).
12307 */
12308RWLockHandle *SessionMachine::lockHandle() const
12309{
12310 AssertReturn(mPeer != NULL, NULL);
12311 return mPeer->lockHandle();
12312}
12313
12314// IInternalMachineControl methods
12315////////////////////////////////////////////////////////////////////////////////
12316
12317/**
12318 * Passes collected guest statistics to performance collector object
12319 */
12320STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12321 ULONG aCpuKernel, ULONG aCpuIdle,
12322 ULONG aMemTotal, ULONG aMemFree,
12323 ULONG aMemBalloon, ULONG aMemShared,
12324 ULONG aMemCache, ULONG aPageTotal,
12325 ULONG aAllocVMM, ULONG aFreeVMM,
12326 ULONG aBalloonedVMM, ULONG aSharedVMM,
12327 ULONG aVmNetRx, ULONG aVmNetTx)
12328{
12329 if (mCollectorGuest)
12330 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12331 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12332 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12333 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12334
12335 return S_OK;
12336}
12337
12338/**
12339 * @note Locks this object for writing.
12340 */
12341STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12342{
12343 AutoCaller autoCaller(this);
12344 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12345
12346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12347
12348 mRemoveSavedState = aRemove;
12349
12350 return S_OK;
12351}
12352
12353/**
12354 * @note Locks the same as #setMachineState() does.
12355 */
12356STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12357{
12358 return setMachineState(aMachineState);
12359}
12360
12361/**
12362 * @note Locks this object for reading.
12363 */
12364STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12365{
12366 AutoCaller autoCaller(this);
12367 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12368
12369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12370
12371#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12372 mIPCSemName.cloneTo(aId);
12373 return S_OK;
12374#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12375# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12376 mIPCKey.cloneTo(aId);
12377# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12378 mData->m_strConfigFileFull.cloneTo(aId);
12379# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12380 return S_OK;
12381#else
12382# error "Port me!"
12383#endif
12384}
12385
12386/**
12387 * @note Locks this object for writing.
12388 */
12389STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12390{
12391 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12392 AutoCaller autoCaller(this);
12393 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12394
12395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12396
12397 if (mData->mSession.mState != SessionState_Locked)
12398 return VBOX_E_INVALID_OBJECT_STATE;
12399
12400 if (!mData->mSession.mProgress.isNull())
12401 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12402
12403 LogFlowThisFunc(("returns S_OK.\n"));
12404 return S_OK;
12405}
12406
12407/**
12408 * @note Locks this object for writing.
12409 */
12410STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12411{
12412 AutoCaller autoCaller(this);
12413 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12414
12415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12416
12417 if (mData->mSession.mState != SessionState_Locked)
12418 return VBOX_E_INVALID_OBJECT_STATE;
12419
12420 /* Finalize the LaunchVMProcess progress object. */
12421 if (mData->mSession.mProgress)
12422 {
12423 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12424 mData->mSession.mProgress.setNull();
12425 }
12426
12427 if (SUCCEEDED((HRESULT)iResult))
12428 {
12429#ifdef VBOX_WITH_RESOURCE_USAGE_API
12430 /* The VM has been powered up successfully, so it makes sense
12431 * now to offer the performance metrics for a running machine
12432 * object. Doing it earlier wouldn't be safe. */
12433 registerMetrics(mParent->performanceCollector(), mPeer,
12434 mData->mSession.mPID);
12435#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12436 }
12437
12438 return S_OK;
12439}
12440
12441/**
12442 * @note Locks this object for writing.
12443 */
12444STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12445{
12446 LogFlowThisFuncEnter();
12447
12448 CheckComArgOutPointerValid(aProgress);
12449
12450 AutoCaller autoCaller(this);
12451 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12452
12453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12454
12455 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12456 E_FAIL);
12457
12458 /* create a progress object to track operation completion */
12459 ComObjPtr<Progress> pProgress;
12460 pProgress.createObject();
12461 pProgress->init(getVirtualBox(),
12462 static_cast<IMachine *>(this) /* aInitiator */,
12463 Bstr(tr("Stopping the virtual machine")).raw(),
12464 FALSE /* aCancelable */);
12465
12466 /* fill in the console task data */
12467 mConsoleTaskData.mLastState = mData->mMachineState;
12468 mConsoleTaskData.mProgress = pProgress;
12469
12470 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12471 setMachineState(MachineState_Stopping);
12472
12473 pProgress.queryInterfaceTo(aProgress);
12474
12475 return S_OK;
12476}
12477
12478/**
12479 * @note Locks this object for writing.
12480 */
12481STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12482{
12483 LogFlowThisFuncEnter();
12484
12485 AutoCaller autoCaller(this);
12486 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12487
12488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12489
12490 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12491 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12492 && mConsoleTaskData.mLastState != MachineState_Null,
12493 E_FAIL);
12494
12495 /*
12496 * On failure, set the state to the state we had when BeginPoweringDown()
12497 * was called (this is expected by Console::PowerDown() and the associated
12498 * task). On success the VM process already changed the state to
12499 * MachineState_PoweredOff, so no need to do anything.
12500 */
12501 if (FAILED(iResult))
12502 setMachineState(mConsoleTaskData.mLastState);
12503
12504 /* notify the progress object about operation completion */
12505 Assert(mConsoleTaskData.mProgress);
12506 if (SUCCEEDED(iResult))
12507 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12508 else
12509 {
12510 Utf8Str strErrMsg(aErrMsg);
12511 if (strErrMsg.length())
12512 mConsoleTaskData.mProgress->notifyComplete(iResult,
12513 COM_IIDOF(ISession),
12514 getComponentName(),
12515 strErrMsg.c_str());
12516 else
12517 mConsoleTaskData.mProgress->notifyComplete(iResult);
12518 }
12519
12520 /* clear out the temporary saved state data */
12521 mConsoleTaskData.mLastState = MachineState_Null;
12522 mConsoleTaskData.mProgress.setNull();
12523
12524 LogFlowThisFuncLeave();
12525 return S_OK;
12526}
12527
12528
12529/**
12530 * Goes through the USB filters of the given machine to see if the given
12531 * device matches any filter or not.
12532 *
12533 * @note Locks the same as USBController::hasMatchingFilter() does.
12534 */
12535STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12536 BOOL *aMatched,
12537 ULONG *aMaskedIfs)
12538{
12539 LogFlowThisFunc(("\n"));
12540
12541 CheckComArgNotNull(aUSBDevice);
12542 CheckComArgOutPointerValid(aMatched);
12543
12544 AutoCaller autoCaller(this);
12545 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12546
12547#ifdef VBOX_WITH_USB
12548 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12549#else
12550 NOREF(aUSBDevice);
12551 NOREF(aMaskedIfs);
12552 *aMatched = FALSE;
12553#endif
12554
12555 return S_OK;
12556}
12557
12558/**
12559 * @note Locks the same as Host::captureUSBDevice() does.
12560 */
12561STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12562{
12563 LogFlowThisFunc(("\n"));
12564
12565 AutoCaller autoCaller(this);
12566 AssertComRCReturnRC(autoCaller.rc());
12567
12568#ifdef VBOX_WITH_USB
12569 /* if captureDeviceForVM() fails, it must have set extended error info */
12570 clearError();
12571 MultiResult rc = mParent->host()->checkUSBProxyService();
12572 if (FAILED(rc)) return rc;
12573
12574 USBProxyService *service = mParent->host()->usbProxyService();
12575 AssertReturn(service, E_FAIL);
12576 return service->captureDeviceForVM(this, Guid(aId).ref());
12577#else
12578 NOREF(aId);
12579 return E_NOTIMPL;
12580#endif
12581}
12582
12583/**
12584 * @note Locks the same as Host::detachUSBDevice() does.
12585 */
12586STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12587{
12588 LogFlowThisFunc(("\n"));
12589
12590 AutoCaller autoCaller(this);
12591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12592
12593#ifdef VBOX_WITH_USB
12594 USBProxyService *service = mParent->host()->usbProxyService();
12595 AssertReturn(service, E_FAIL);
12596 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12597#else
12598 NOREF(aId);
12599 NOREF(aDone);
12600 return E_NOTIMPL;
12601#endif
12602}
12603
12604/**
12605 * Inserts all machine filters to the USB proxy service and then calls
12606 * Host::autoCaptureUSBDevices().
12607 *
12608 * Called by Console from the VM process upon VM startup.
12609 *
12610 * @note Locks what called methods lock.
12611 */
12612STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12613{
12614 LogFlowThisFunc(("\n"));
12615
12616 AutoCaller autoCaller(this);
12617 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12618
12619#ifdef VBOX_WITH_USB
12620 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12621 AssertComRC(rc);
12622 NOREF(rc);
12623
12624 USBProxyService *service = mParent->host()->usbProxyService();
12625 AssertReturn(service, E_FAIL);
12626 return service->autoCaptureDevicesForVM(this);
12627#else
12628 return S_OK;
12629#endif
12630}
12631
12632/**
12633 * Removes all machine filters from the USB proxy service and then calls
12634 * Host::detachAllUSBDevices().
12635 *
12636 * Called by Console from the VM process upon normal VM termination or by
12637 * SessionMachine::uninit() upon abnormal VM termination (from under the
12638 * Machine/SessionMachine lock).
12639 *
12640 * @note Locks what called methods lock.
12641 */
12642STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12643{
12644 LogFlowThisFunc(("\n"));
12645
12646 AutoCaller autoCaller(this);
12647 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12648
12649#ifdef VBOX_WITH_USB
12650 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12651 AssertComRC(rc);
12652 NOREF(rc);
12653
12654 USBProxyService *service = mParent->host()->usbProxyService();
12655 AssertReturn(service, E_FAIL);
12656 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12657#else
12658 NOREF(aDone);
12659 return S_OK;
12660#endif
12661}
12662
12663/**
12664 * @note Locks this object for writing.
12665 */
12666STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12667 IProgress **aProgress)
12668{
12669 LogFlowThisFuncEnter();
12670
12671 AssertReturn(aSession, E_INVALIDARG);
12672 AssertReturn(aProgress, E_INVALIDARG);
12673
12674 AutoCaller autoCaller(this);
12675
12676 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12677 /*
12678 * We don't assert below because it might happen that a non-direct session
12679 * informs us it is closed right after we've been uninitialized -- it's ok.
12680 */
12681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12682
12683 /* get IInternalSessionControl interface */
12684 ComPtr<IInternalSessionControl> control(aSession);
12685
12686 ComAssertRet(!control.isNull(), E_INVALIDARG);
12687
12688 /* Creating a Progress object requires the VirtualBox lock, and
12689 * thus locking it here is required by the lock order rules. */
12690 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12691
12692 if (control == mData->mSession.mDirectControl)
12693 {
12694 ComAssertRet(aProgress, E_POINTER);
12695
12696 /* The direct session is being normally closed by the client process
12697 * ----------------------------------------------------------------- */
12698
12699 /* go to the closing state (essential for all open*Session() calls and
12700 * for #checkForDeath()) */
12701 Assert(mData->mSession.mState == SessionState_Locked);
12702 mData->mSession.mState = SessionState_Unlocking;
12703
12704 /* set direct control to NULL to release the remote instance */
12705 mData->mSession.mDirectControl.setNull();
12706 LogFlowThisFunc(("Direct control is set to NULL\n"));
12707
12708 if (mData->mSession.mProgress)
12709 {
12710 /* finalize the progress, someone might wait if a frontend
12711 * closes the session before powering on the VM. */
12712 mData->mSession.mProgress->notifyComplete(E_FAIL,
12713 COM_IIDOF(ISession),
12714 getComponentName(),
12715 tr("The VM session was closed before any attempt to power it on"));
12716 mData->mSession.mProgress.setNull();
12717 }
12718
12719 /* Create the progress object the client will use to wait until
12720 * #checkForDeath() is called to uninitialize this session object after
12721 * it releases the IPC semaphore.
12722 * Note! Because we're "reusing" mProgress here, this must be a proxy
12723 * object just like for LaunchVMProcess. */
12724 Assert(mData->mSession.mProgress.isNull());
12725 ComObjPtr<ProgressProxy> progress;
12726 progress.createObject();
12727 ComPtr<IUnknown> pPeer(mPeer);
12728 progress->init(mParent, pPeer,
12729 Bstr(tr("Closing session")).raw(),
12730 FALSE /* aCancelable */);
12731 progress.queryInterfaceTo(aProgress);
12732 mData->mSession.mProgress = progress;
12733 }
12734 else
12735 {
12736 /* the remote session is being normally closed */
12737 Data::Session::RemoteControlList::iterator it =
12738 mData->mSession.mRemoteControls.begin();
12739 while (it != mData->mSession.mRemoteControls.end())
12740 {
12741 if (control == *it)
12742 break;
12743 ++it;
12744 }
12745 BOOL found = it != mData->mSession.mRemoteControls.end();
12746 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12747 E_INVALIDARG);
12748 // This MUST be erase(it), not remove(*it) as the latter triggers a
12749 // very nasty use after free due to the place where the value "lives".
12750 mData->mSession.mRemoteControls.erase(it);
12751 }
12752
12753 /* signal the client watcher thread, because the client is going away */
12754 mParent->updateClientWatcher();
12755
12756 LogFlowThisFuncLeave();
12757 return S_OK;
12758}
12759
12760/**
12761 * @note Locks this object for writing.
12762 */
12763STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12764{
12765 LogFlowThisFuncEnter();
12766
12767 CheckComArgOutPointerValid(aProgress);
12768 CheckComArgOutPointerValid(aStateFilePath);
12769
12770 AutoCaller autoCaller(this);
12771 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12772
12773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12774
12775 AssertReturn( mData->mMachineState == MachineState_Paused
12776 && mConsoleTaskData.mLastState == MachineState_Null
12777 && mConsoleTaskData.strStateFilePath.isEmpty(),
12778 E_FAIL);
12779
12780 /* create a progress object to track operation completion */
12781 ComObjPtr<Progress> pProgress;
12782 pProgress.createObject();
12783 pProgress->init(getVirtualBox(),
12784 static_cast<IMachine *>(this) /* aInitiator */,
12785 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12786 FALSE /* aCancelable */);
12787
12788 Utf8Str strStateFilePath;
12789 /* stateFilePath is null when the machine is not running */
12790 if (mData->mMachineState == MachineState_Paused)
12791 composeSavedStateFilename(strStateFilePath);
12792
12793 /* fill in the console task data */
12794 mConsoleTaskData.mLastState = mData->mMachineState;
12795 mConsoleTaskData.strStateFilePath = strStateFilePath;
12796 mConsoleTaskData.mProgress = pProgress;
12797
12798 /* set the state to Saving (this is expected by Console::SaveState()) */
12799 setMachineState(MachineState_Saving);
12800
12801 strStateFilePath.cloneTo(aStateFilePath);
12802 pProgress.queryInterfaceTo(aProgress);
12803
12804 return S_OK;
12805}
12806
12807/**
12808 * @note Locks mParent + this object for writing.
12809 */
12810STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12811{
12812 LogFlowThisFunc(("\n"));
12813
12814 AutoCaller autoCaller(this);
12815 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12816
12817 /* endSavingState() need mParent lock */
12818 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12819
12820 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12821 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12822 && mConsoleTaskData.mLastState != MachineState_Null
12823 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12824 E_FAIL);
12825
12826 /*
12827 * On failure, set the state to the state we had when BeginSavingState()
12828 * was called (this is expected by Console::SaveState() and the associated
12829 * task). On success the VM process already changed the state to
12830 * MachineState_Saved, so no need to do anything.
12831 */
12832 if (FAILED(iResult))
12833 setMachineState(mConsoleTaskData.mLastState);
12834
12835 return endSavingState(iResult, aErrMsg);
12836}
12837
12838/**
12839 * @note Locks this object for writing.
12840 */
12841STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12842{
12843 LogFlowThisFunc(("\n"));
12844
12845 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12846
12847 AutoCaller autoCaller(this);
12848 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12849
12850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12851
12852 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12853 || mData->mMachineState == MachineState_Teleported
12854 || mData->mMachineState == MachineState_Aborted
12855 , E_FAIL); /** @todo setError. */
12856
12857 Utf8Str stateFilePathFull = aSavedStateFile;
12858 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12859 if (RT_FAILURE(vrc))
12860 return setError(VBOX_E_FILE_ERROR,
12861 tr("Invalid saved state file path '%ls' (%Rrc)"),
12862 aSavedStateFile,
12863 vrc);
12864
12865 mSSData->strStateFilePath = stateFilePathFull;
12866
12867 /* The below setMachineState() will detect the state transition and will
12868 * update the settings file */
12869
12870 return setMachineState(MachineState_Saved);
12871}
12872
12873STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12874 ComSafeArrayOut(BSTR, aValues),
12875 ComSafeArrayOut(LONG64, aTimestamps),
12876 ComSafeArrayOut(BSTR, aFlags))
12877{
12878 LogFlowThisFunc(("\n"));
12879
12880#ifdef VBOX_WITH_GUEST_PROPS
12881 using namespace guestProp;
12882
12883 AutoCaller autoCaller(this);
12884 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12885
12886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12887
12888 CheckComArgOutSafeArrayPointerValid(aNames);
12889 CheckComArgOutSafeArrayPointerValid(aValues);
12890 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12891 CheckComArgOutSafeArrayPointerValid(aFlags);
12892
12893 size_t cEntries = mHWData->mGuestProperties.size();
12894 com::SafeArray<BSTR> names(cEntries);
12895 com::SafeArray<BSTR> values(cEntries);
12896 com::SafeArray<LONG64> timestamps(cEntries);
12897 com::SafeArray<BSTR> flags(cEntries);
12898 unsigned i = 0;
12899 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12900 it != mHWData->mGuestProperties.end();
12901 ++it)
12902 {
12903 char szFlags[MAX_FLAGS_LEN + 1];
12904 it->first.cloneTo(&names[i]);
12905 it->second.strValue.cloneTo(&values[i]);
12906 timestamps[i] = it->second.mTimestamp;
12907 /* If it is NULL, keep it NULL. */
12908 if (it->second.mFlags)
12909 {
12910 writeFlags(it->second.mFlags, szFlags);
12911 Bstr(szFlags).cloneTo(&flags[i]);
12912 }
12913 else
12914 flags[i] = NULL;
12915 ++i;
12916 }
12917 names.detachTo(ComSafeArrayOutArg(aNames));
12918 values.detachTo(ComSafeArrayOutArg(aValues));
12919 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12920 flags.detachTo(ComSafeArrayOutArg(aFlags));
12921 return S_OK;
12922#else
12923 ReturnComNotImplemented();
12924#endif
12925}
12926
12927STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12928 IN_BSTR aValue,
12929 LONG64 aTimestamp,
12930 IN_BSTR aFlags)
12931{
12932 LogFlowThisFunc(("\n"));
12933
12934#ifdef VBOX_WITH_GUEST_PROPS
12935 using namespace guestProp;
12936
12937 CheckComArgStrNotEmptyOrNull(aName);
12938 CheckComArgNotNull(aValue);
12939 CheckComArgNotNull(aFlags);
12940
12941 try
12942 {
12943 /*
12944 * Convert input up front.
12945 */
12946 Utf8Str utf8Name(aName);
12947 uint32_t fFlags = NILFLAG;
12948 if (aFlags)
12949 {
12950 Utf8Str utf8Flags(aFlags);
12951 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12952 AssertRCReturn(vrc, E_INVALIDARG);
12953 }
12954
12955 /*
12956 * Now grab the object lock, validate the state and do the update.
12957 */
12958 AutoCaller autoCaller(this);
12959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12960
12961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12962
12963 switch (mData->mMachineState)
12964 {
12965 case MachineState_Paused:
12966 case MachineState_Running:
12967 case MachineState_Teleporting:
12968 case MachineState_TeleportingPausedVM:
12969 case MachineState_LiveSnapshotting:
12970 case MachineState_DeletingSnapshotOnline:
12971 case MachineState_DeletingSnapshotPaused:
12972 case MachineState_Saving:
12973 case MachineState_Stopping:
12974 break;
12975
12976 default:
12977 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12978 VBOX_E_INVALID_VM_STATE);
12979 }
12980
12981 setModified(IsModified_MachineData);
12982 mHWData.backup();
12983
12984 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
12985 if (it != mHWData->mGuestProperties.end())
12986 {
12987 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
12988 {
12989 it->second.strValue = aValue;
12990 it->second.mFlags = fFlags;
12991 it->second.mTimestamp = aTimestamp;
12992 }
12993 else
12994 mHWData->mGuestProperties.erase(it);
12995
12996 mData->mGuestPropertiesModified = TRUE;
12997 }
12998
12999 /*
13000 * Send a callback notification if appropriate
13001 */
13002 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13003 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13004 RTSTR_MAX,
13005 utf8Name.c_str(),
13006 RTSTR_MAX, NULL)
13007 )
13008 {
13009 alock.release();
13010
13011 mParent->onGuestPropertyChange(mData->mUuid,
13012 aName,
13013 aValue,
13014 aFlags);
13015 }
13016 }
13017 catch (...)
13018 {
13019 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13020 }
13021 return S_OK;
13022#else
13023 ReturnComNotImplemented();
13024#endif
13025}
13026
13027STDMETHODIMP SessionMachine::LockMedia()
13028{
13029 AutoCaller autoCaller(this);
13030 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13031
13032 AutoMultiWriteLock2 alock(this->lockHandle(),
13033 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13034
13035 AssertReturn( mData->mMachineState == MachineState_Starting
13036 || mData->mMachineState == MachineState_Restoring
13037 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13038
13039 clearError();
13040 alock.release();
13041 return lockMedia();
13042}
13043
13044STDMETHODIMP SessionMachine::UnlockMedia()
13045{
13046 unlockMedia();
13047 return S_OK;
13048}
13049
13050STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13051 IMediumAttachment **aNewAttachment)
13052{
13053 CheckComArgNotNull(aAttachment);
13054 CheckComArgOutPointerValid(aNewAttachment);
13055
13056 AutoCaller autoCaller(this);
13057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13058
13059 // request the host lock first, since might be calling Host methods for getting host drives;
13060 // next, protect the media tree all the while we're in here, as well as our member variables
13061 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13062 this->lockHandle(),
13063 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13064
13065 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13066
13067 Bstr ctrlName;
13068 LONG lPort;
13069 LONG lDevice;
13070 bool fTempEject;
13071 {
13072 AutoCaller autoAttachCaller(this);
13073 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13074
13075 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13076
13077 /* Need to query the details first, as the IMediumAttachment reference
13078 * might be to the original settings, which we are going to change. */
13079 ctrlName = pAttach->getControllerName();
13080 lPort = pAttach->getPort();
13081 lDevice = pAttach->getDevice();
13082 fTempEject = pAttach->getTempEject();
13083 }
13084
13085 if (!fTempEject)
13086 {
13087 /* Remember previously mounted medium. The medium before taking the
13088 * backup is not necessarily the same thing. */
13089 ComObjPtr<Medium> oldmedium;
13090 oldmedium = pAttach->getMedium();
13091
13092 setModified(IsModified_Storage);
13093 mMediaData.backup();
13094
13095 // The backup operation makes the pAttach reference point to the
13096 // old settings. Re-get the correct reference.
13097 pAttach = findAttachment(mMediaData->mAttachments,
13098 ctrlName.raw(),
13099 lPort,
13100 lDevice);
13101
13102 {
13103 AutoCaller autoAttachCaller(this);
13104 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13105
13106 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13107 if (!oldmedium.isNull())
13108 oldmedium->removeBackReference(mData->mUuid);
13109
13110 pAttach->updateMedium(NULL);
13111 pAttach->updateEjected();
13112 }
13113
13114 setModified(IsModified_Storage);
13115 }
13116 else
13117 {
13118 {
13119 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13120 pAttach->updateEjected();
13121 }
13122 }
13123
13124 pAttach.queryInterfaceTo(aNewAttachment);
13125
13126 return S_OK;
13127}
13128
13129// public methods only for internal purposes
13130/////////////////////////////////////////////////////////////////////////////
13131
13132/**
13133 * Called from the client watcher thread to check for expected or unexpected
13134 * death of the client process that has a direct session to this machine.
13135 *
13136 * On Win32 and on OS/2, this method is called only when we've got the
13137 * mutex (i.e. the client has either died or terminated normally) so it always
13138 * returns @c true (the client is terminated, the session machine is
13139 * uninitialized).
13140 *
13141 * On other platforms, the method returns @c true if the client process has
13142 * terminated normally or abnormally and the session machine was uninitialized,
13143 * and @c false if the client process is still alive.
13144 *
13145 * @note Locks this object for writing.
13146 */
13147bool SessionMachine::checkForDeath()
13148{
13149 Uninit::Reason reason;
13150 bool terminated = false;
13151
13152 /* Enclose autoCaller with a block because calling uninit() from under it
13153 * will deadlock. */
13154 {
13155 AutoCaller autoCaller(this);
13156 if (!autoCaller.isOk())
13157 {
13158 /* return true if not ready, to cause the client watcher to exclude
13159 * the corresponding session from watching */
13160 LogFlowThisFunc(("Already uninitialized!\n"));
13161 return true;
13162 }
13163
13164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13165
13166 /* Determine the reason of death: if the session state is Closing here,
13167 * everything is fine. Otherwise it means that the client did not call
13168 * OnSessionEnd() before it released the IPC semaphore. This may happen
13169 * either because the client process has abnormally terminated, or
13170 * because it simply forgot to call ISession::Close() before exiting. We
13171 * threat the latter also as an abnormal termination (see
13172 * Session::uninit() for details). */
13173 reason = mData->mSession.mState == SessionState_Unlocking ?
13174 Uninit::Normal :
13175 Uninit::Abnormal;
13176
13177#if defined(RT_OS_WINDOWS)
13178
13179 AssertMsg(mIPCSem, ("semaphore must be created"));
13180
13181 /* release the IPC mutex */
13182 ::ReleaseMutex(mIPCSem);
13183
13184 terminated = true;
13185
13186#elif defined(RT_OS_OS2)
13187
13188 AssertMsg(mIPCSem, ("semaphore must be created"));
13189
13190 /* release the IPC mutex */
13191 ::DosReleaseMutexSem(mIPCSem);
13192
13193 terminated = true;
13194
13195#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13196
13197 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13198
13199 int val = ::semctl(mIPCSem, 0, GETVAL);
13200 if (val > 0)
13201 {
13202 /* the semaphore is signaled, meaning the session is terminated */
13203 terminated = true;
13204 }
13205
13206#else
13207# error "Port me!"
13208#endif
13209
13210 } /* AutoCaller block */
13211
13212 if (terminated)
13213 uninit(reason);
13214
13215 return terminated;
13216}
13217
13218/**
13219 * @note Locks this object for reading.
13220 */
13221HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13222{
13223 LogFlowThisFunc(("\n"));
13224
13225 AutoCaller autoCaller(this);
13226 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13227
13228 ComPtr<IInternalSessionControl> directControl;
13229 {
13230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13231 directControl = mData->mSession.mDirectControl;
13232 }
13233
13234 /* ignore notifications sent after #OnSessionEnd() is called */
13235 if (!directControl)
13236 return S_OK;
13237
13238 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13239}
13240
13241/**
13242 * @note Locks this object for reading.
13243 */
13244HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13245 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13246{
13247 LogFlowThisFunc(("\n"));
13248
13249 AutoCaller autoCaller(this);
13250 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13251
13252 ComPtr<IInternalSessionControl> directControl;
13253 {
13254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13255 directControl = mData->mSession.mDirectControl;
13256 }
13257
13258 /* ignore notifications sent after #OnSessionEnd() is called */
13259 if (!directControl)
13260 return S_OK;
13261 /*
13262 * instead acting like callback we ask IVirtualBox deliver corresponding event
13263 */
13264
13265 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13266 return S_OK;
13267}
13268
13269/**
13270 * @note Locks this object for reading.
13271 */
13272HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13273{
13274 LogFlowThisFunc(("\n"));
13275
13276 AutoCaller autoCaller(this);
13277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13278
13279 ComPtr<IInternalSessionControl> directControl;
13280 {
13281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13282 directControl = mData->mSession.mDirectControl;
13283 }
13284
13285 /* ignore notifications sent after #OnSessionEnd() is called */
13286 if (!directControl)
13287 return S_OK;
13288
13289 return directControl->OnSerialPortChange(serialPort);
13290}
13291
13292/**
13293 * @note Locks this object for reading.
13294 */
13295HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13296{
13297 LogFlowThisFunc(("\n"));
13298
13299 AutoCaller autoCaller(this);
13300 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13301
13302 ComPtr<IInternalSessionControl> directControl;
13303 {
13304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13305 directControl = mData->mSession.mDirectControl;
13306 }
13307
13308 /* ignore notifications sent after #OnSessionEnd() is called */
13309 if (!directControl)
13310 return S_OK;
13311
13312 return directControl->OnParallelPortChange(parallelPort);
13313}
13314
13315/**
13316 * @note Locks this object for reading.
13317 */
13318HRESULT SessionMachine::onStorageControllerChange()
13319{
13320 LogFlowThisFunc(("\n"));
13321
13322 AutoCaller autoCaller(this);
13323 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13324
13325 ComPtr<IInternalSessionControl> directControl;
13326 {
13327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13328 directControl = mData->mSession.mDirectControl;
13329 }
13330
13331 /* ignore notifications sent after #OnSessionEnd() is called */
13332 if (!directControl)
13333 return S_OK;
13334
13335 return directControl->OnStorageControllerChange();
13336}
13337
13338/**
13339 * @note Locks this object for reading.
13340 */
13341HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13342{
13343 LogFlowThisFunc(("\n"));
13344
13345 AutoCaller autoCaller(this);
13346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13347
13348 ComPtr<IInternalSessionControl> directControl;
13349 {
13350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13351 directControl = mData->mSession.mDirectControl;
13352 }
13353
13354 /* ignore notifications sent after #OnSessionEnd() is called */
13355 if (!directControl)
13356 return S_OK;
13357
13358 return directControl->OnMediumChange(aAttachment, aForce);
13359}
13360
13361/**
13362 * @note Locks this object for reading.
13363 */
13364HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13365{
13366 LogFlowThisFunc(("\n"));
13367
13368 AutoCaller autoCaller(this);
13369 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13370
13371 ComPtr<IInternalSessionControl> directControl;
13372 {
13373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13374 directControl = mData->mSession.mDirectControl;
13375 }
13376
13377 /* ignore notifications sent after #OnSessionEnd() is called */
13378 if (!directControl)
13379 return S_OK;
13380
13381 return directControl->OnCPUChange(aCPU, aRemove);
13382}
13383
13384HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13385{
13386 LogFlowThisFunc(("\n"));
13387
13388 AutoCaller autoCaller(this);
13389 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13390
13391 ComPtr<IInternalSessionControl> directControl;
13392 {
13393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13394 directControl = mData->mSession.mDirectControl;
13395 }
13396
13397 /* ignore notifications sent after #OnSessionEnd() is called */
13398 if (!directControl)
13399 return S_OK;
13400
13401 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13402}
13403
13404/**
13405 * @note Locks this object for reading.
13406 */
13407HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13408{
13409 LogFlowThisFunc(("\n"));
13410
13411 AutoCaller autoCaller(this);
13412 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13413
13414 ComPtr<IInternalSessionControl> directControl;
13415 {
13416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13417 directControl = mData->mSession.mDirectControl;
13418 }
13419
13420 /* ignore notifications sent after #OnSessionEnd() is called */
13421 if (!directControl)
13422 return S_OK;
13423
13424 return directControl->OnVRDEServerChange(aRestart);
13425}
13426
13427/**
13428 * @note Locks this object for reading.
13429 */
13430HRESULT SessionMachine::onUSBControllerChange()
13431{
13432 LogFlowThisFunc(("\n"));
13433
13434 AutoCaller autoCaller(this);
13435 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13436
13437 ComPtr<IInternalSessionControl> directControl;
13438 {
13439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13440 directControl = mData->mSession.mDirectControl;
13441 }
13442
13443 /* ignore notifications sent after #OnSessionEnd() is called */
13444 if (!directControl)
13445 return S_OK;
13446
13447 return directControl->OnUSBControllerChange();
13448}
13449
13450/**
13451 * @note Locks this object for reading.
13452 */
13453HRESULT SessionMachine::onSharedFolderChange()
13454{
13455 LogFlowThisFunc(("\n"));
13456
13457 AutoCaller autoCaller(this);
13458 AssertComRCReturnRC(autoCaller.rc());
13459
13460 ComPtr<IInternalSessionControl> directControl;
13461 {
13462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13463 directControl = mData->mSession.mDirectControl;
13464 }
13465
13466 /* ignore notifications sent after #OnSessionEnd() is called */
13467 if (!directControl)
13468 return S_OK;
13469
13470 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13471}
13472
13473/**
13474 * @note Locks this object for reading.
13475 */
13476HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13477{
13478 LogFlowThisFunc(("\n"));
13479
13480 AutoCaller autoCaller(this);
13481 AssertComRCReturnRC(autoCaller.rc());
13482
13483 ComPtr<IInternalSessionControl> directControl;
13484 {
13485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13486 directControl = mData->mSession.mDirectControl;
13487 }
13488
13489 /* ignore notifications sent after #OnSessionEnd() is called */
13490 if (!directControl)
13491 return S_OK;
13492
13493 return directControl->OnClipboardModeChange(aClipboardMode);
13494}
13495
13496/**
13497 * @note Locks this object for reading.
13498 */
13499HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13500{
13501 LogFlowThisFunc(("\n"));
13502
13503 AutoCaller autoCaller(this);
13504 AssertComRCReturnRC(autoCaller.rc());
13505
13506 ComPtr<IInternalSessionControl> directControl;
13507 {
13508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13509 directControl = mData->mSession.mDirectControl;
13510 }
13511
13512 /* ignore notifications sent after #OnSessionEnd() is called */
13513 if (!directControl)
13514 return S_OK;
13515
13516 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13517}
13518
13519/**
13520 * @note Locks this object for reading.
13521 */
13522HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13523{
13524 LogFlowThisFunc(("\n"));
13525
13526 AutoCaller autoCaller(this);
13527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13528
13529 ComPtr<IInternalSessionControl> directControl;
13530 {
13531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13532 directControl = mData->mSession.mDirectControl;
13533 }
13534
13535 /* ignore notifications sent after #OnSessionEnd() is called */
13536 if (!directControl)
13537 return S_OK;
13538
13539 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13540}
13541
13542/**
13543 * @note Locks this object for reading.
13544 */
13545HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13546{
13547 LogFlowThisFunc(("\n"));
13548
13549 AutoCaller autoCaller(this);
13550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13551
13552 ComPtr<IInternalSessionControl> directControl;
13553 {
13554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13555 directControl = mData->mSession.mDirectControl;
13556 }
13557
13558 /* ignore notifications sent after #OnSessionEnd() is called */
13559 if (!directControl)
13560 return S_OK;
13561
13562 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13563}
13564
13565/**
13566 * Returns @c true if this machine's USB controller reports it has a matching
13567 * filter for the given USB device and @c false otherwise.
13568 *
13569 * @note locks this object for reading.
13570 */
13571bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13572{
13573 AutoCaller autoCaller(this);
13574 /* silently return if not ready -- this method may be called after the
13575 * direct machine session has been called */
13576 if (!autoCaller.isOk())
13577 return false;
13578
13579#ifdef VBOX_WITH_USB
13580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13581
13582 switch (mData->mMachineState)
13583 {
13584 case MachineState_Starting:
13585 case MachineState_Restoring:
13586 case MachineState_TeleportingIn:
13587 case MachineState_Paused:
13588 case MachineState_Running:
13589 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13590 * elsewhere... */
13591 alock.release();
13592 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13593 default: break;
13594 }
13595#else
13596 NOREF(aDevice);
13597 NOREF(aMaskedIfs);
13598#endif
13599 return false;
13600}
13601
13602/**
13603 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13604 */
13605HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13606 IVirtualBoxErrorInfo *aError,
13607 ULONG aMaskedIfs)
13608{
13609 LogFlowThisFunc(("\n"));
13610
13611 AutoCaller autoCaller(this);
13612
13613 /* This notification may happen after the machine object has been
13614 * uninitialized (the session was closed), so don't assert. */
13615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13616
13617 ComPtr<IInternalSessionControl> directControl;
13618 {
13619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13620 directControl = mData->mSession.mDirectControl;
13621 }
13622
13623 /* fail on notifications sent after #OnSessionEnd() is called, it is
13624 * expected by the caller */
13625 if (!directControl)
13626 return E_FAIL;
13627
13628 /* No locks should be held at this point. */
13629 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13630 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13631
13632 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13633}
13634
13635/**
13636 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13637 */
13638HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13639 IVirtualBoxErrorInfo *aError)
13640{
13641 LogFlowThisFunc(("\n"));
13642
13643 AutoCaller autoCaller(this);
13644
13645 /* This notification may happen after the machine object has been
13646 * uninitialized (the session was closed), so don't assert. */
13647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13648
13649 ComPtr<IInternalSessionControl> directControl;
13650 {
13651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13652 directControl = mData->mSession.mDirectControl;
13653 }
13654
13655 /* fail on notifications sent after #OnSessionEnd() is called, it is
13656 * expected by the caller */
13657 if (!directControl)
13658 return E_FAIL;
13659
13660 /* No locks should be held at this point. */
13661 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13662 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13663
13664 return directControl->OnUSBDeviceDetach(aId, aError);
13665}
13666
13667// protected methods
13668/////////////////////////////////////////////////////////////////////////////
13669
13670/**
13671 * Helper method to finalize saving the state.
13672 *
13673 * @note Must be called from under this object's lock.
13674 *
13675 * @param aRc S_OK if the snapshot has been taken successfully
13676 * @param aErrMsg human readable error message for failure
13677 *
13678 * @note Locks mParent + this objects for writing.
13679 */
13680HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13681{
13682 LogFlowThisFuncEnter();
13683
13684 AutoCaller autoCaller(this);
13685 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13686
13687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13688
13689 HRESULT rc = S_OK;
13690
13691 if (SUCCEEDED(aRc))
13692 {
13693 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13694
13695 /* save all VM settings */
13696 rc = saveSettings(NULL);
13697 // no need to check whether VirtualBox.xml needs saving also since
13698 // we can't have a name change pending at this point
13699 }
13700 else
13701 {
13702 // delete the saved state file (it might have been already created);
13703 // we need not check whether this is shared with a snapshot here because
13704 // we certainly created this saved state file here anew
13705 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13706 }
13707
13708 /* notify the progress object about operation completion */
13709 Assert(mConsoleTaskData.mProgress);
13710 if (SUCCEEDED(aRc))
13711 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13712 else
13713 {
13714 if (aErrMsg.length())
13715 mConsoleTaskData.mProgress->notifyComplete(aRc,
13716 COM_IIDOF(ISession),
13717 getComponentName(),
13718 aErrMsg.c_str());
13719 else
13720 mConsoleTaskData.mProgress->notifyComplete(aRc);
13721 }
13722
13723 /* clear out the temporary saved state data */
13724 mConsoleTaskData.mLastState = MachineState_Null;
13725 mConsoleTaskData.strStateFilePath.setNull();
13726 mConsoleTaskData.mProgress.setNull();
13727
13728 LogFlowThisFuncLeave();
13729 return rc;
13730}
13731
13732/**
13733 * Deletes the given file if it is no longer in use by either the current machine state
13734 * (if the machine is "saved") or any of the machine's snapshots.
13735 *
13736 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13737 * but is different for each SnapshotMachine. When calling this, the order of calling this
13738 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13739 * is therefore critical. I know, it's all rather messy.
13740 *
13741 * @param strStateFile
13742 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13743 */
13744void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13745 Snapshot *pSnapshotToIgnore)
13746{
13747 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13748 if ( (strStateFile.isNotEmpty())
13749 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13750 )
13751 // ... and it must also not be shared with other snapshots
13752 if ( !mData->mFirstSnapshot
13753 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13754 // this checks the SnapshotMachine's state file paths
13755 )
13756 RTFileDelete(strStateFile.c_str());
13757}
13758
13759/**
13760 * Locks the attached media.
13761 *
13762 * All attached hard disks are locked for writing and DVD/floppy are locked for
13763 * reading. Parents of attached hard disks (if any) are locked for reading.
13764 *
13765 * This method also performs accessibility check of all media it locks: if some
13766 * media is inaccessible, the method will return a failure and a bunch of
13767 * extended error info objects per each inaccessible medium.
13768 *
13769 * Note that this method is atomic: if it returns a success, all media are
13770 * locked as described above; on failure no media is locked at all (all
13771 * succeeded individual locks will be undone).
13772 *
13773 * The caller is responsible for doing the necessary state sanity checks.
13774 *
13775 * The locks made by this method must be undone by calling #unlockMedia() when
13776 * no more needed.
13777 */
13778HRESULT SessionMachine::lockMedia()
13779{
13780 AutoCaller autoCaller(this);
13781 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13782
13783 AutoMultiWriteLock2 alock(this->lockHandle(),
13784 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13785
13786 /* bail out if trying to lock things with already set up locking */
13787 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13788
13789 MultiResult mrc(S_OK);
13790
13791 /* Collect locking information for all medium objects attached to the VM. */
13792 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13793 it != mMediaData->mAttachments.end();
13794 ++it)
13795 {
13796 MediumAttachment* pAtt = *it;
13797 DeviceType_T devType = pAtt->getType();
13798 Medium *pMedium = pAtt->getMedium();
13799
13800 MediumLockList *pMediumLockList(new MediumLockList());
13801 // There can be attachments without a medium (floppy/dvd), and thus
13802 // it's impossible to create a medium lock list. It still makes sense
13803 // to have the empty medium lock list in the map in case a medium is
13804 // attached later.
13805 if (pMedium != NULL)
13806 {
13807 MediumType_T mediumType = pMedium->getType();
13808 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13809 || mediumType == MediumType_Shareable;
13810 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13811
13812 alock.release();
13813 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13814 !fIsReadOnlyLock /* fMediumLockWrite */,
13815 NULL,
13816 *pMediumLockList);
13817 alock.acquire();
13818 if (FAILED(mrc))
13819 {
13820 delete pMediumLockList;
13821 mData->mSession.mLockedMedia.Clear();
13822 break;
13823 }
13824 }
13825
13826 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13827 if (FAILED(rc))
13828 {
13829 mData->mSession.mLockedMedia.Clear();
13830 mrc = setError(rc,
13831 tr("Collecting locking information for all attached media failed"));
13832 break;
13833 }
13834 }
13835
13836 if (SUCCEEDED(mrc))
13837 {
13838 /* Now lock all media. If this fails, nothing is locked. */
13839 alock.release();
13840 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13841 alock.acquire();
13842 if (FAILED(rc))
13843 {
13844 mrc = setError(rc,
13845 tr("Locking of attached media failed"));
13846 }
13847 }
13848
13849 return mrc;
13850}
13851
13852/**
13853 * Undoes the locks made by by #lockMedia().
13854 */
13855void SessionMachine::unlockMedia()
13856{
13857 AutoCaller autoCaller(this);
13858 AssertComRCReturnVoid(autoCaller.rc());
13859
13860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13861
13862 /* we may be holding important error info on the current thread;
13863 * preserve it */
13864 ErrorInfoKeeper eik;
13865
13866 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13867 AssertComRC(rc);
13868}
13869
13870/**
13871 * Helper to change the machine state (reimplementation).
13872 *
13873 * @note Locks this object for writing.
13874 * @note This method must not call saveSettings or SaveSettings, otherwise
13875 * it can cause crashes in random places due to unexpectedly committing
13876 * the current settings. The caller is responsible for that. The call
13877 * to saveStateSettings is fine, because this method does not commit.
13878 */
13879HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13880{
13881 LogFlowThisFuncEnter();
13882 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13883
13884 AutoCaller autoCaller(this);
13885 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13886
13887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13888
13889 MachineState_T oldMachineState = mData->mMachineState;
13890
13891 AssertMsgReturn(oldMachineState != aMachineState,
13892 ("oldMachineState=%s, aMachineState=%s\n",
13893 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13894 E_FAIL);
13895
13896 HRESULT rc = S_OK;
13897
13898 int stsFlags = 0;
13899 bool deleteSavedState = false;
13900
13901 /* detect some state transitions */
13902
13903 if ( ( oldMachineState == MachineState_Saved
13904 && aMachineState == MachineState_Restoring)
13905 || ( ( oldMachineState == MachineState_PoweredOff
13906 || oldMachineState == MachineState_Teleported
13907 || oldMachineState == MachineState_Aborted
13908 )
13909 && ( aMachineState == MachineState_TeleportingIn
13910 || aMachineState == MachineState_Starting
13911 )
13912 )
13913 )
13914 {
13915 /* The EMT thread is about to start */
13916
13917 /* Nothing to do here for now... */
13918
13919 /// @todo NEWMEDIA don't let mDVDDrive and other children
13920 /// change anything when in the Starting/Restoring state
13921 }
13922 else if ( ( oldMachineState == MachineState_Running
13923 || oldMachineState == MachineState_Paused
13924 || oldMachineState == MachineState_Teleporting
13925 || oldMachineState == MachineState_LiveSnapshotting
13926 || oldMachineState == MachineState_Stuck
13927 || oldMachineState == MachineState_Starting
13928 || oldMachineState == MachineState_Stopping
13929 || oldMachineState == MachineState_Saving
13930 || oldMachineState == MachineState_Restoring
13931 || oldMachineState == MachineState_TeleportingPausedVM
13932 || oldMachineState == MachineState_TeleportingIn
13933 )
13934 && ( aMachineState == MachineState_PoweredOff
13935 || aMachineState == MachineState_Saved
13936 || aMachineState == MachineState_Teleported
13937 || aMachineState == MachineState_Aborted
13938 )
13939 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13940 * snapshot */
13941 && ( mConsoleTaskData.mSnapshot.isNull()
13942 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13943 )
13944 )
13945 {
13946 /* The EMT thread has just stopped, unlock attached media. Note that as
13947 * opposed to locking that is done from Console, we do unlocking here
13948 * because the VM process may have aborted before having a chance to
13949 * properly unlock all media it locked. */
13950
13951 unlockMedia();
13952 }
13953
13954 if (oldMachineState == MachineState_Restoring)
13955 {
13956 if (aMachineState != MachineState_Saved)
13957 {
13958 /*
13959 * delete the saved state file once the machine has finished
13960 * restoring from it (note that Console sets the state from
13961 * Restoring to Saved if the VM couldn't restore successfully,
13962 * to give the user an ability to fix an error and retry --
13963 * we keep the saved state file in this case)
13964 */
13965 deleteSavedState = true;
13966 }
13967 }
13968 else if ( oldMachineState == MachineState_Saved
13969 && ( aMachineState == MachineState_PoweredOff
13970 || aMachineState == MachineState_Aborted
13971 || aMachineState == MachineState_Teleported
13972 )
13973 )
13974 {
13975 /*
13976 * delete the saved state after Console::ForgetSavedState() is called
13977 * or if the VM process (owning a direct VM session) crashed while the
13978 * VM was Saved
13979 */
13980
13981 /// @todo (dmik)
13982 // Not sure that deleting the saved state file just because of the
13983 // client death before it attempted to restore the VM is a good
13984 // thing. But when it crashes we need to go to the Aborted state
13985 // which cannot have the saved state file associated... The only
13986 // way to fix this is to make the Aborted condition not a VM state
13987 // but a bool flag: i.e., when a crash occurs, set it to true and
13988 // change the state to PoweredOff or Saved depending on the
13989 // saved state presence.
13990
13991 deleteSavedState = true;
13992 mData->mCurrentStateModified = TRUE;
13993 stsFlags |= SaveSTS_CurStateModified;
13994 }
13995
13996 if ( aMachineState == MachineState_Starting
13997 || aMachineState == MachineState_Restoring
13998 || aMachineState == MachineState_TeleportingIn
13999 )
14000 {
14001 /* set the current state modified flag to indicate that the current
14002 * state is no more identical to the state in the
14003 * current snapshot */
14004 if (!mData->mCurrentSnapshot.isNull())
14005 {
14006 mData->mCurrentStateModified = TRUE;
14007 stsFlags |= SaveSTS_CurStateModified;
14008 }
14009 }
14010
14011 if (deleteSavedState)
14012 {
14013 if (mRemoveSavedState)
14014 {
14015 Assert(!mSSData->strStateFilePath.isEmpty());
14016
14017 // it is safe to delete the saved state file if ...
14018 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14019 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14020 // ... none of the snapshots share the saved state file
14021 )
14022 RTFileDelete(mSSData->strStateFilePath.c_str());
14023 }
14024
14025 mSSData->strStateFilePath.setNull();
14026 stsFlags |= SaveSTS_StateFilePath;
14027 }
14028
14029 /* redirect to the underlying peer machine */
14030 mPeer->setMachineState(aMachineState);
14031
14032 if ( aMachineState == MachineState_PoweredOff
14033 || aMachineState == MachineState_Teleported
14034 || aMachineState == MachineState_Aborted
14035 || aMachineState == MachineState_Saved)
14036 {
14037 /* the machine has stopped execution
14038 * (or the saved state file was adopted) */
14039 stsFlags |= SaveSTS_StateTimeStamp;
14040 }
14041
14042 if ( ( oldMachineState == MachineState_PoweredOff
14043 || oldMachineState == MachineState_Aborted
14044 || oldMachineState == MachineState_Teleported
14045 )
14046 && aMachineState == MachineState_Saved)
14047 {
14048 /* the saved state file was adopted */
14049 Assert(!mSSData->strStateFilePath.isEmpty());
14050 stsFlags |= SaveSTS_StateFilePath;
14051 }
14052
14053#ifdef VBOX_WITH_GUEST_PROPS
14054 if ( aMachineState == MachineState_PoweredOff
14055 || aMachineState == MachineState_Aborted
14056 || aMachineState == MachineState_Teleported)
14057 {
14058 /* Make sure any transient guest properties get removed from the
14059 * property store on shutdown. */
14060
14061 HWData::GuestPropertyMap::const_iterator it;
14062 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14063 if (!fNeedsSaving)
14064 for (it = mHWData->mGuestProperties.begin();
14065 it != mHWData->mGuestProperties.end(); ++it)
14066 if ( (it->second.mFlags & guestProp::TRANSIENT)
14067 || (it->second.mFlags & guestProp::TRANSRESET))
14068 {
14069 fNeedsSaving = true;
14070 break;
14071 }
14072 if (fNeedsSaving)
14073 {
14074 mData->mCurrentStateModified = TRUE;
14075 stsFlags |= SaveSTS_CurStateModified;
14076 }
14077 }
14078#endif
14079
14080 rc = saveStateSettings(stsFlags);
14081
14082 if ( ( oldMachineState != MachineState_PoweredOff
14083 && oldMachineState != MachineState_Aborted
14084 && oldMachineState != MachineState_Teleported
14085 )
14086 && ( aMachineState == MachineState_PoweredOff
14087 || aMachineState == MachineState_Aborted
14088 || aMachineState == MachineState_Teleported
14089 )
14090 )
14091 {
14092 /* we've been shut down for any reason */
14093 /* no special action so far */
14094 }
14095
14096 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14097 LogFlowThisFuncLeave();
14098 return rc;
14099}
14100
14101/**
14102 * Sends the current machine state value to the VM process.
14103 *
14104 * @note Locks this object for reading, then calls a client process.
14105 */
14106HRESULT SessionMachine::updateMachineStateOnClient()
14107{
14108 AutoCaller autoCaller(this);
14109 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14110
14111 ComPtr<IInternalSessionControl> directControl;
14112 {
14113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14114 AssertReturn(!!mData, E_FAIL);
14115 directControl = mData->mSession.mDirectControl;
14116
14117 /* directControl may be already set to NULL here in #OnSessionEnd()
14118 * called too early by the direct session process while there is still
14119 * some operation (like deleting the snapshot) in progress. The client
14120 * process in this case is waiting inside Session::close() for the
14121 * "end session" process object to complete, while #uninit() called by
14122 * #checkForDeath() on the Watcher thread is waiting for the pending
14123 * operation to complete. For now, we accept this inconsistent behavior
14124 * and simply do nothing here. */
14125
14126 if (mData->mSession.mState == SessionState_Unlocking)
14127 return S_OK;
14128
14129 AssertReturn(!directControl.isNull(), E_FAIL);
14130 }
14131
14132 return directControl->UpdateMachineState(mData->mMachineState);
14133}
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