VirtualBox

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

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

Main/SystemProperties+Machine: new config setting for default VM frontend.
Frontend/VirtualBox+VBoxManage: changes to use the default VM frontend when starting a VM, other minor cleanups
Main/xml/*.xsd: attempt to bring the XML schema close to reality
doc/manual: document the new possibilities, and fix a few long standing inaccuracies

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 472.5 KB
Line 
1/* $Id: MachineImpl.cpp 44948 2013-03-07 10:36:42Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 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 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3778 bool fHotplug = false;
3779 if (Global::IsOnlineOrTransient(mData->mMachineState))
3780 fHotplug = true;
3781
3782 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3783 return setError(VBOX_E_INVALID_VM_STATE,
3784 tr("Controller '%ls' does not support hotplugging"),
3785 aControllerName);
3786
3787 // check that the port and device are not out of range
3788 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3789 if (FAILED(rc)) return rc;
3790
3791 /* check if the device slot is already busy */
3792 MediumAttachment *pAttachTemp;
3793 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3794 aControllerName,
3795 aControllerPort,
3796 aDevice)))
3797 {
3798 Medium *pMedium = pAttachTemp->getMedium();
3799 if (pMedium)
3800 {
3801 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3802 return setError(VBOX_E_OBJECT_IN_USE,
3803 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3804 pMedium->getLocationFull().c_str(),
3805 aControllerPort,
3806 aDevice,
3807 aControllerName);
3808 }
3809 else
3810 return setError(VBOX_E_OBJECT_IN_USE,
3811 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3812 aControllerPort, aDevice, aControllerName);
3813 }
3814
3815 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3816 if (aMedium && medium.isNull())
3817 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3818
3819 AutoCaller mediumCaller(medium);
3820 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3821
3822 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3823
3824 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3825 && !medium.isNull()
3826 )
3827 return setError(VBOX_E_OBJECT_IN_USE,
3828 tr("Medium '%s' is already attached to this virtual machine"),
3829 medium->getLocationFull().c_str());
3830
3831 if (!medium.isNull())
3832 {
3833 MediumType_T mtype = medium->getType();
3834 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3835 // For DVDs it's not written to the config file, so needs no global config
3836 // version bump. For floppies it's a new attribute "type", which is ignored
3837 // by older VirtualBox version, so needs no global config version bump either.
3838 // For hard disks this type is not accepted.
3839 if (mtype == MediumType_MultiAttach)
3840 {
3841 // This type is new with VirtualBox 4.0 and therefore requires settings
3842 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3843 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3844 // two reasons: The medium type is a property of the media registry tree, which
3845 // can reside in the global config file (for pre-4.0 media); we would therefore
3846 // possibly need to bump the global config version. We don't want to do that though
3847 // because that might make downgrading to pre-4.0 impossible.
3848 // As a result, we can only use these two new types if the medium is NOT in the
3849 // global registry:
3850 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3851 if ( medium->isInRegistry(uuidGlobalRegistry)
3852 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3853 )
3854 return setError(VBOX_E_INVALID_OBJECT_STATE,
3855 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3856 "to machines that were created with VirtualBox 4.0 or later"),
3857 medium->getLocationFull().c_str());
3858 }
3859 }
3860
3861 bool fIndirect = false;
3862 if (!medium.isNull())
3863 fIndirect = medium->isReadOnly();
3864 bool associate = true;
3865
3866 do
3867 {
3868 if ( aType == DeviceType_HardDisk
3869 && mMediaData.isBackedUp())
3870 {
3871 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3872
3873 /* check if the medium was attached to the VM before we started
3874 * changing attachments in which case the attachment just needs to
3875 * be restored */
3876 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3877 {
3878 AssertReturn(!fIndirect, E_FAIL);
3879
3880 /* see if it's the same bus/channel/device */
3881 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3882 {
3883 /* the simplest case: restore the whole attachment
3884 * and return, nothing else to do */
3885 mMediaData->mAttachments.push_back(pAttachTemp);
3886 return S_OK;
3887 }
3888
3889 /* bus/channel/device differ; we need a new attachment object,
3890 * but don't try to associate it again */
3891 associate = false;
3892 break;
3893 }
3894 }
3895
3896 /* go further only if the attachment is to be indirect */
3897 if (!fIndirect)
3898 break;
3899
3900 /* perform the so called smart attachment logic for indirect
3901 * attachments. Note that smart attachment is only applicable to base
3902 * hard disks. */
3903
3904 if (medium->getParent().isNull())
3905 {
3906 /* first, investigate the backup copy of the current hard disk
3907 * attachments to make it possible to re-attach existing diffs to
3908 * another device slot w/o losing their contents */
3909 if (mMediaData.isBackedUp())
3910 {
3911 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3912
3913 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3914 uint32_t foundLevel = 0;
3915
3916 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3917 it != oldAtts.end();
3918 ++it)
3919 {
3920 uint32_t level = 0;
3921 MediumAttachment *pAttach = *it;
3922 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3923 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3924 if (pMedium.isNull())
3925 continue;
3926
3927 if (pMedium->getBase(&level) == medium)
3928 {
3929 /* skip the hard disk if its currently attached (we
3930 * cannot attach the same hard disk twice) */
3931 if (findAttachment(mMediaData->mAttachments,
3932 pMedium))
3933 continue;
3934
3935 /* matched device, channel and bus (i.e. attached to the
3936 * same place) will win and immediately stop the search;
3937 * otherwise the attachment that has the youngest
3938 * descendant of medium will be used
3939 */
3940 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3941 {
3942 /* the simplest case: restore the whole attachment
3943 * and return, nothing else to do */
3944 mMediaData->mAttachments.push_back(*it);
3945 return S_OK;
3946 }
3947 else if ( foundIt == oldAtts.end()
3948 || level > foundLevel /* prefer younger */
3949 )
3950 {
3951 foundIt = it;
3952 foundLevel = level;
3953 }
3954 }
3955 }
3956
3957 if (foundIt != oldAtts.end())
3958 {
3959 /* use the previously attached hard disk */
3960 medium = (*foundIt)->getMedium();
3961 mediumCaller.attach(medium);
3962 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3963 mediumLock.attach(medium);
3964 /* not implicit, doesn't require association with this VM */
3965 fIndirect = false;
3966 associate = false;
3967 /* go right to the MediumAttachment creation */
3968 break;
3969 }
3970 }
3971
3972 /* must give up the medium lock and medium tree lock as below we
3973 * go over snapshots, which needs a lock with higher lock order. */
3974 mediumLock.release();
3975 treeLock.release();
3976
3977 /* then, search through snapshots for the best diff in the given
3978 * hard disk's chain to base the new diff on */
3979
3980 ComObjPtr<Medium> base;
3981 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3982 while (snap)
3983 {
3984 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3985
3986 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3987
3988 MediumAttachment *pAttachFound = NULL;
3989 uint32_t foundLevel = 0;
3990
3991 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3992 it != snapAtts.end();
3993 ++it)
3994 {
3995 MediumAttachment *pAttach = *it;
3996 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3997 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3998 if (pMedium.isNull())
3999 continue;
4000
4001 uint32_t level = 0;
4002 if (pMedium->getBase(&level) == medium)
4003 {
4004 /* matched device, channel and bus (i.e. attached to the
4005 * same place) will win and immediately stop the search;
4006 * otherwise the attachment that has the youngest
4007 * descendant of medium will be used
4008 */
4009 if ( pAttach->getDevice() == aDevice
4010 && pAttach->getPort() == aControllerPort
4011 && pAttach->getControllerName() == aControllerName
4012 )
4013 {
4014 pAttachFound = pAttach;
4015 break;
4016 }
4017 else if ( !pAttachFound
4018 || level > foundLevel /* prefer younger */
4019 )
4020 {
4021 pAttachFound = pAttach;
4022 foundLevel = level;
4023 }
4024 }
4025 }
4026
4027 if (pAttachFound)
4028 {
4029 base = pAttachFound->getMedium();
4030 break;
4031 }
4032
4033 snap = snap->getParent();
4034 }
4035
4036 /* re-lock medium tree and the medium, as we need it below */
4037 treeLock.acquire();
4038 mediumLock.acquire();
4039
4040 /* found a suitable diff, use it as a base */
4041 if (!base.isNull())
4042 {
4043 medium = base;
4044 mediumCaller.attach(medium);
4045 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4046 mediumLock.attach(medium);
4047 }
4048 }
4049
4050 Utf8Str strFullSnapshotFolder;
4051 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4052
4053 ComObjPtr<Medium> diff;
4054 diff.createObject();
4055 // store this diff in the same registry as the parent
4056 Guid uuidRegistryParent;
4057 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4058 {
4059 // parent image has no registry: this can happen if we're attaching a new immutable
4060 // image that has not yet been attached (medium then points to the base and we're
4061 // creating the diff image for the immutable, and the parent is not yet registered);
4062 // put the parent in the machine registry then
4063 mediumLock.release();
4064 treeLock.release();
4065 alock.release();
4066 addMediumToRegistry(medium);
4067 alock.acquire();
4068 treeLock.acquire();
4069 mediumLock.acquire();
4070 medium->getFirstRegistryMachineId(uuidRegistryParent);
4071 }
4072 rc = diff->init(mParent,
4073 medium->getPreferredDiffFormat(),
4074 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4075 uuidRegistryParent);
4076 if (FAILED(rc)) return rc;
4077
4078 /* Apply the normal locking logic to the entire chain. */
4079 MediumLockList *pMediumLockList(new MediumLockList());
4080 mediumLock.release();
4081 treeLock.release();
4082 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4083 true /* fMediumLockWrite */,
4084 medium,
4085 *pMediumLockList);
4086 treeLock.acquire();
4087 mediumLock.acquire();
4088 if (SUCCEEDED(rc))
4089 {
4090 mediumLock.release();
4091 treeLock.release();
4092 rc = pMediumLockList->Lock();
4093 treeLock.acquire();
4094 mediumLock.acquire();
4095 if (FAILED(rc))
4096 setError(rc,
4097 tr("Could not lock medium when creating diff '%s'"),
4098 diff->getLocationFull().c_str());
4099 else
4100 {
4101 /* will release the lock before the potentially lengthy
4102 * operation, so protect with the special state */
4103 MachineState_T oldState = mData->mMachineState;
4104 setMachineState(MachineState_SettingUp);
4105
4106 mediumLock.release();
4107 treeLock.release();
4108 alock.release();
4109
4110 rc = medium->createDiffStorage(diff,
4111 MediumVariant_Standard,
4112 pMediumLockList,
4113 NULL /* aProgress */,
4114 true /* aWait */);
4115
4116 alock.acquire();
4117 treeLock.acquire();
4118 mediumLock.acquire();
4119
4120 setMachineState(oldState);
4121 }
4122 }
4123
4124 /* Unlock the media and free the associated memory. */
4125 delete pMediumLockList;
4126
4127 if (FAILED(rc)) return rc;
4128
4129 /* use the created diff for the actual attachment */
4130 medium = diff;
4131 mediumCaller.attach(medium);
4132 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4133 mediumLock.attach(medium);
4134 }
4135 while (0);
4136
4137 ComObjPtr<MediumAttachment> attachment;
4138 attachment.createObject();
4139 rc = attachment->init(this,
4140 medium,
4141 aControllerName,
4142 aControllerPort,
4143 aDevice,
4144 aType,
4145 fIndirect,
4146 false /* fPassthrough */,
4147 false /* fTempEject */,
4148 false /* fNonRotational */,
4149 false /* fDiscard */,
4150 Utf8Str::Empty);
4151 if (FAILED(rc)) return rc;
4152
4153 if (associate && !medium.isNull())
4154 {
4155 // as the last step, associate the medium to the VM
4156 rc = medium->addBackReference(mData->mUuid);
4157 // here we can fail because of Deleting, or being in process of creating a Diff
4158 if (FAILED(rc)) return rc;
4159
4160 mediumLock.release();
4161 treeLock.release();
4162 alock.release();
4163 addMediumToRegistry(medium);
4164 alock.acquire();
4165 treeLock.acquire();
4166 mediumLock.acquire();
4167 }
4168
4169 /* success: finally remember the attachment */
4170 setModified(IsModified_Storage);
4171 mMediaData.backup();
4172 mMediaData->mAttachments.push_back(attachment);
4173
4174 mediumLock.release();
4175 treeLock.release();
4176 alock.release();
4177
4178 if (fHotplug)
4179 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4180
4181 mParent->saveModifiedRegistries();
4182
4183 return rc;
4184}
4185
4186STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4187 LONG aDevice)
4188{
4189 CheckComArgStrNotEmptyOrNull(aControllerName);
4190
4191 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4192 aControllerName, aControllerPort, aDevice));
4193
4194 AutoCaller autoCaller(this);
4195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4196
4197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4198
4199 HRESULT rc = checkStateDependency(MutableStateDep);
4200 if (FAILED(rc)) return rc;
4201
4202 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4203
4204 /* Check for an existing controller. */
4205 ComObjPtr<StorageController> ctl;
4206 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4207 if (FAILED(rc)) return rc;
4208
4209 StorageControllerType_T ctrlType;
4210 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4211 if (FAILED(rc))
4212 return setError(E_FAIL,
4213 tr("Could not get type of controller '%ls'"),
4214 aControllerName);
4215
4216 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4217 bool fHotplug = false;
4218 if (Global::IsOnlineOrTransient(mData->mMachineState))
4219 fHotplug = true;
4220
4221 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4222 return setError(VBOX_E_INVALID_VM_STATE,
4223 tr("Controller '%ls' does not support hotplugging"),
4224 aControllerName);
4225
4226 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4227 aControllerName,
4228 aControllerPort,
4229 aDevice);
4230 if (!pAttach)
4231 return setError(VBOX_E_OBJECT_NOT_FOUND,
4232 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4233 aDevice, aControllerPort, aControllerName);
4234
4235 /*
4236 * The VM has to detach the device before we delete any implicit diffs.
4237 * If this fails we can roll back without loosing data.
4238 */
4239 if (fHotplug)
4240 {
4241 alock.release();
4242 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4243 alock.acquire();
4244 }
4245 if (FAILED(rc)) return rc;
4246
4247 /* If we are here everything went well and we can delete the implicit now. */
4248 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4249
4250 alock.release();
4251
4252 mParent->saveModifiedRegistries();
4253
4254 return rc;
4255}
4256
4257STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4258 LONG aDevice, BOOL aPassthrough)
4259{
4260 CheckComArgStrNotEmptyOrNull(aControllerName);
4261
4262 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4263 aControllerName, aControllerPort, aDevice, aPassthrough));
4264
4265 AutoCaller autoCaller(this);
4266 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4267
4268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4269
4270 HRESULT rc = checkStateDependency(MutableStateDep);
4271 if (FAILED(rc)) return rc;
4272
4273 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4274
4275 if (Global::IsOnlineOrTransient(mData->mMachineState))
4276 return setError(VBOX_E_INVALID_VM_STATE,
4277 tr("Invalid machine state: %s"),
4278 Global::stringifyMachineState(mData->mMachineState));
4279
4280 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4281 aControllerName,
4282 aControllerPort,
4283 aDevice);
4284 if (!pAttach)
4285 return setError(VBOX_E_OBJECT_NOT_FOUND,
4286 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4287 aDevice, aControllerPort, aControllerName);
4288
4289
4290 setModified(IsModified_Storage);
4291 mMediaData.backup();
4292
4293 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4294
4295 if (pAttach->getType() != DeviceType_DVD)
4296 return setError(E_INVALIDARG,
4297 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4298 aDevice, aControllerPort, aControllerName);
4299 pAttach->updatePassthrough(!!aPassthrough);
4300
4301 return S_OK;
4302}
4303
4304STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4305 LONG aDevice, BOOL aTemporaryEject)
4306{
4307 CheckComArgStrNotEmptyOrNull(aControllerName);
4308
4309 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4310 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4311
4312 AutoCaller autoCaller(this);
4313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4314
4315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4316
4317 HRESULT rc = checkStateDependency(MutableStateDep);
4318 if (FAILED(rc)) return rc;
4319
4320 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4321 aControllerName,
4322 aControllerPort,
4323 aDevice);
4324 if (!pAttach)
4325 return setError(VBOX_E_OBJECT_NOT_FOUND,
4326 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4327 aDevice, aControllerPort, aControllerName);
4328
4329
4330 setModified(IsModified_Storage);
4331 mMediaData.backup();
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 if (pAttach->getType() != DeviceType_DVD)
4336 return setError(E_INVALIDARG,
4337 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4338 aDevice, aControllerPort, aControllerName);
4339 pAttach->updateTempEject(!!aTemporaryEject);
4340
4341 return S_OK;
4342}
4343
4344STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4345 LONG aDevice, BOOL aNonRotational)
4346{
4347 CheckComArgStrNotEmptyOrNull(aControllerName);
4348
4349 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4350 aControllerName, aControllerPort, aDevice, aNonRotational));
4351
4352 AutoCaller autoCaller(this);
4353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4354
4355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4356
4357 HRESULT rc = checkStateDependency(MutableStateDep);
4358 if (FAILED(rc)) return rc;
4359
4360 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4361
4362 if (Global::IsOnlineOrTransient(mData->mMachineState))
4363 return setError(VBOX_E_INVALID_VM_STATE,
4364 tr("Invalid machine state: %s"),
4365 Global::stringifyMachineState(mData->mMachineState));
4366
4367 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4368 aControllerName,
4369 aControllerPort,
4370 aDevice);
4371 if (!pAttach)
4372 return setError(VBOX_E_OBJECT_NOT_FOUND,
4373 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4374 aDevice, aControllerPort, aControllerName);
4375
4376
4377 setModified(IsModified_Storage);
4378 mMediaData.backup();
4379
4380 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4381
4382 if (pAttach->getType() != DeviceType_HardDisk)
4383 return setError(E_INVALIDARG,
4384 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"),
4385 aDevice, aControllerPort, aControllerName);
4386 pAttach->updateNonRotational(!!aNonRotational);
4387
4388 return S_OK;
4389}
4390
4391STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4392 LONG aDevice, BOOL aDiscard)
4393{
4394 CheckComArgStrNotEmptyOrNull(aControllerName);
4395
4396 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4397 aControllerName, aControllerPort, aDevice, aDiscard));
4398
4399 AutoCaller autoCaller(this);
4400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4401
4402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4403
4404 HRESULT rc = checkStateDependency(MutableStateDep);
4405 if (FAILED(rc)) return rc;
4406
4407 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4408
4409 if (Global::IsOnlineOrTransient(mData->mMachineState))
4410 return setError(VBOX_E_INVALID_VM_STATE,
4411 tr("Invalid machine state: %s"),
4412 Global::stringifyMachineState(mData->mMachineState));
4413
4414 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4415 aControllerName,
4416 aControllerPort,
4417 aDevice);
4418 if (!pAttach)
4419 return setError(VBOX_E_OBJECT_NOT_FOUND,
4420 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4421 aDevice, aControllerPort, aControllerName);
4422
4423
4424 setModified(IsModified_Storage);
4425 mMediaData.backup();
4426
4427 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4428
4429 if (pAttach->getType() != DeviceType_HardDisk)
4430 return setError(E_INVALIDARG,
4431 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"),
4432 aDevice, aControllerPort, aControllerName);
4433 pAttach->updateDiscard(!!aDiscard);
4434
4435 return S_OK;
4436}
4437
4438STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4439 LONG aDevice)
4440{
4441 int rc = S_OK;
4442 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4443 aControllerName, aControllerPort, aDevice));
4444
4445 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4446
4447 return rc;
4448}
4449
4450STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4451 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4452{
4453 CheckComArgStrNotEmptyOrNull(aControllerName);
4454
4455 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4456 aControllerName, aControllerPort, aDevice));
4457
4458 AutoCaller autoCaller(this);
4459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4460
4461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4462
4463 HRESULT rc = checkStateDependency(MutableStateDep);
4464 if (FAILED(rc)) return rc;
4465
4466 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4467
4468 if (Global::IsOnlineOrTransient(mData->mMachineState))
4469 return setError(VBOX_E_INVALID_VM_STATE,
4470 tr("Invalid machine state: %s"),
4471 Global::stringifyMachineState(mData->mMachineState));
4472
4473 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4474 aControllerName,
4475 aControllerPort,
4476 aDevice);
4477 if (!pAttach)
4478 return setError(VBOX_E_OBJECT_NOT_FOUND,
4479 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4480 aDevice, aControllerPort, aControllerName);
4481
4482
4483 setModified(IsModified_Storage);
4484 mMediaData.backup();
4485
4486 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4487 if (aBandwidthGroup && group.isNull())
4488 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4489
4490 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4491
4492 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4493 if (strBandwidthGroupOld.isNotEmpty())
4494 {
4495 /* Get the bandwidth group object and release it - this must not fail. */
4496 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4497 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4498 Assert(SUCCEEDED(rc));
4499
4500 pBandwidthGroupOld->release();
4501 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4502 }
4503
4504 if (!group.isNull())
4505 {
4506 group->reference();
4507 pAttach->updateBandwidthGroup(group->getName());
4508 }
4509
4510 return S_OK;
4511}
4512
4513STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4514 LONG aControllerPort,
4515 LONG aDevice,
4516 DeviceType_T aType)
4517{
4518 HRESULT rc = S_OK;
4519
4520 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4521 aControllerName, aControllerPort, aDevice, aType));
4522
4523 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4524
4525 return rc;
4526}
4527
4528
4529
4530STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4531 LONG aControllerPort,
4532 LONG aDevice,
4533 BOOL aForce)
4534{
4535 int rc = S_OK;
4536 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4537 aControllerName, aControllerPort, aForce));
4538
4539 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4540
4541 return rc;
4542}
4543
4544STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4545 LONG aControllerPort,
4546 LONG aDevice,
4547 IMedium *aMedium,
4548 BOOL aForce)
4549{
4550 int rc = S_OK;
4551 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4552 aControllerName, aControllerPort, aDevice, aForce));
4553
4554 CheckComArgStrNotEmptyOrNull(aControllerName);
4555
4556 AutoCaller autoCaller(this);
4557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4558
4559 // request the host lock first, since might be calling Host methods for getting host drives;
4560 // next, protect the media tree all the while we're in here, as well as our member variables
4561 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4562 this->lockHandle(),
4563 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4564
4565 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4566 aControllerName,
4567 aControllerPort,
4568 aDevice);
4569 if (pAttach.isNull())
4570 return setError(VBOX_E_OBJECT_NOT_FOUND,
4571 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4572 aDevice, aControllerPort, aControllerName);
4573
4574 /* Remember previously mounted medium. The medium before taking the
4575 * backup is not necessarily the same thing. */
4576 ComObjPtr<Medium> oldmedium;
4577 oldmedium = pAttach->getMedium();
4578
4579 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4580 if (aMedium && pMedium.isNull())
4581 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4582
4583 AutoCaller mediumCaller(pMedium);
4584 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4585
4586 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4587 if (pMedium)
4588 {
4589 DeviceType_T mediumType = pAttach->getType();
4590 switch (mediumType)
4591 {
4592 case DeviceType_DVD:
4593 case DeviceType_Floppy:
4594 break;
4595
4596 default:
4597 return setError(VBOX_E_INVALID_OBJECT_STATE,
4598 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4599 aControllerPort,
4600 aDevice,
4601 aControllerName);
4602 }
4603 }
4604
4605 setModified(IsModified_Storage);
4606 mMediaData.backup();
4607
4608 {
4609 // The backup operation makes the pAttach reference point to the
4610 // old settings. Re-get the correct reference.
4611 pAttach = findAttachment(mMediaData->mAttachments,
4612 aControllerName,
4613 aControllerPort,
4614 aDevice);
4615 if (!oldmedium.isNull())
4616 oldmedium->removeBackReference(mData->mUuid);
4617 if (!pMedium.isNull())
4618 {
4619 pMedium->addBackReference(mData->mUuid);
4620
4621 mediumLock.release();
4622 multiLock.release();
4623 addMediumToRegistry(pMedium);
4624 multiLock.acquire();
4625 mediumLock.acquire();
4626 }
4627
4628 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4629 pAttach->updateMedium(pMedium);
4630 }
4631
4632 setModified(IsModified_Storage);
4633
4634 mediumLock.release();
4635 multiLock.release();
4636 rc = onMediumChange(pAttach, aForce);
4637 multiLock.acquire();
4638 mediumLock.acquire();
4639
4640 /* On error roll back this change only. */
4641 if (FAILED(rc))
4642 {
4643 if (!pMedium.isNull())
4644 pMedium->removeBackReference(mData->mUuid);
4645 pAttach = findAttachment(mMediaData->mAttachments,
4646 aControllerName,
4647 aControllerPort,
4648 aDevice);
4649 /* If the attachment is gone in the meantime, bail out. */
4650 if (pAttach.isNull())
4651 return rc;
4652 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4653 if (!oldmedium.isNull())
4654 oldmedium->addBackReference(mData->mUuid);
4655 pAttach->updateMedium(oldmedium);
4656 }
4657
4658 mediumLock.release();
4659 multiLock.release();
4660
4661 mParent->saveModifiedRegistries();
4662
4663 return rc;
4664}
4665
4666STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4667 LONG aControllerPort,
4668 LONG aDevice,
4669 IMedium **aMedium)
4670{
4671 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4672 aControllerName, aControllerPort, aDevice));
4673
4674 CheckComArgStrNotEmptyOrNull(aControllerName);
4675 CheckComArgOutPointerValid(aMedium);
4676
4677 AutoCaller autoCaller(this);
4678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4679
4680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 *aMedium = NULL;
4683
4684 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4685 aControllerName,
4686 aControllerPort,
4687 aDevice);
4688 if (pAttach.isNull())
4689 return setError(VBOX_E_OBJECT_NOT_FOUND,
4690 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4691 aDevice, aControllerPort, aControllerName);
4692
4693 pAttach->getMedium().queryInterfaceTo(aMedium);
4694
4695 return S_OK;
4696}
4697
4698STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4699{
4700 CheckComArgOutPointerValid(port);
4701 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4702
4703 AutoCaller autoCaller(this);
4704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4705
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 mSerialPorts[slot].queryInterfaceTo(port);
4709
4710 return S_OK;
4711}
4712
4713STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4714{
4715 CheckComArgOutPointerValid(port);
4716 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4717
4718 AutoCaller autoCaller(this);
4719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4720
4721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4722
4723 mParallelPorts[slot].queryInterfaceTo(port);
4724
4725 return S_OK;
4726}
4727
4728STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4729{
4730 CheckComArgOutPointerValid(adapter);
4731 /* Do not assert if slot is out of range, just return the advertised
4732 status. testdriver/vbox.py triggers this in logVmInfo. */
4733 if (slot >= mNetworkAdapters.size())
4734 return setError(E_INVALIDARG,
4735 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4736 slot, mNetworkAdapters.size());
4737
4738 AutoCaller autoCaller(this);
4739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4740
4741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4744
4745 return S_OK;
4746}
4747
4748STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4749{
4750 CheckComArgOutSafeArrayPointerValid(aKeys);
4751
4752 AutoCaller autoCaller(this);
4753 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4754
4755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4756
4757 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4758 int i = 0;
4759 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4760 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4761 ++it, ++i)
4762 {
4763 const Utf8Str &strKey = it->first;
4764 strKey.cloneTo(&saKeys[i]);
4765 }
4766 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4767
4768 return S_OK;
4769 }
4770
4771 /**
4772 * @note Locks this object for reading.
4773 */
4774STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4775 BSTR *aValue)
4776{
4777 CheckComArgStrNotEmptyOrNull(aKey);
4778 CheckComArgOutPointerValid(aValue);
4779
4780 AutoCaller autoCaller(this);
4781 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4782
4783 /* start with nothing found */
4784 Bstr bstrResult("");
4785
4786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4787
4788 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4789 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4790 // found:
4791 bstrResult = it->second; // source is a Utf8Str
4792
4793 /* return the result to caller (may be empty) */
4794 bstrResult.cloneTo(aValue);
4795
4796 return S_OK;
4797}
4798
4799 /**
4800 * @note Locks mParent for writing + this object for writing.
4801 */
4802STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4803{
4804 CheckComArgStrNotEmptyOrNull(aKey);
4805
4806 AutoCaller autoCaller(this);
4807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4808
4809 Utf8Str strKey(aKey);
4810 Utf8Str strValue(aValue);
4811 Utf8Str strOldValue; // empty
4812
4813 // locking note: we only hold the read lock briefly to look up the old value,
4814 // then release it and call the onExtraCanChange callbacks. There is a small
4815 // chance of a race insofar as the callback might be called twice if two callers
4816 // change the same key at the same time, but that's a much better solution
4817 // than the deadlock we had here before. The actual changing of the extradata
4818 // is then performed under the write lock and race-free.
4819
4820 // look up the old value first; if nothing has changed then we need not do anything
4821 {
4822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4823 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4824 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4825 strOldValue = it->second;
4826 }
4827
4828 bool fChanged;
4829 if ((fChanged = (strOldValue != strValue)))
4830 {
4831 // ask for permission from all listeners outside the locks;
4832 // onExtraDataCanChange() only briefly requests the VirtualBox
4833 // lock to copy the list of callbacks to invoke
4834 Bstr error;
4835 Bstr bstrValue(aValue);
4836
4837 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4838 {
4839 const char *sep = error.isEmpty() ? "" : ": ";
4840 CBSTR err = error.raw();
4841 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4842 sep, err));
4843 return setError(E_ACCESSDENIED,
4844 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4845 aKey,
4846 bstrValue.raw(),
4847 sep,
4848 err);
4849 }
4850
4851 // data is changing and change not vetoed: then write it out under the lock
4852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4853
4854 if (isSnapshotMachine())
4855 {
4856 HRESULT rc = checkStateDependency(MutableStateDep);
4857 if (FAILED(rc)) return rc;
4858 }
4859
4860 if (strValue.isEmpty())
4861 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4862 else
4863 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4864 // creates a new key if needed
4865
4866 bool fNeedsGlobalSaveSettings = false;
4867 saveSettings(&fNeedsGlobalSaveSettings);
4868
4869 if (fNeedsGlobalSaveSettings)
4870 {
4871 // save the global settings; for that we should hold only the VirtualBox lock
4872 alock.release();
4873 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4874 mParent->saveSettings();
4875 }
4876 }
4877
4878 // fire notification outside the lock
4879 if (fChanged)
4880 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4881
4882 return S_OK;
4883}
4884
4885STDMETHODIMP Machine::SaveSettings()
4886{
4887 AutoCaller autoCaller(this);
4888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4889
4890 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4891
4892 /* when there was auto-conversion, we want to save the file even if
4893 * the VM is saved */
4894 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4895 if (FAILED(rc)) return rc;
4896
4897 /* the settings file path may never be null */
4898 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4899
4900 /* save all VM data excluding snapshots */
4901 bool fNeedsGlobalSaveSettings = false;
4902 rc = saveSettings(&fNeedsGlobalSaveSettings);
4903 mlock.release();
4904
4905 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4906 {
4907 // save the global settings; for that we should hold only the VirtualBox lock
4908 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4909 rc = mParent->saveSettings();
4910 }
4911
4912 return rc;
4913}
4914
4915STDMETHODIMP Machine::DiscardSettings()
4916{
4917 AutoCaller autoCaller(this);
4918 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4919
4920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4921
4922 HRESULT rc = checkStateDependency(MutableStateDep);
4923 if (FAILED(rc)) return rc;
4924
4925 /*
4926 * during this rollback, the session will be notified if data has
4927 * been actually changed
4928 */
4929 rollback(true /* aNotify */);
4930
4931 return S_OK;
4932}
4933
4934/** @note Locks objects! */
4935STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4936 ComSafeArrayOut(IMedium*, aMedia))
4937{
4938 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4939 AutoLimitedCaller autoCaller(this);
4940 AssertComRCReturnRC(autoCaller.rc());
4941
4942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4943
4944 Guid id(getId());
4945
4946 if (mData->mSession.mState != SessionState_Unlocked)
4947 return setError(VBOX_E_INVALID_OBJECT_STATE,
4948 tr("Cannot unregister the machine '%s' while it is locked"),
4949 mUserData->s.strName.c_str());
4950
4951 // wait for state dependents to drop to zero
4952 ensureNoStateDependencies();
4953
4954 if (!mData->mAccessible)
4955 {
4956 // inaccessible maschines can only be unregistered; uninitialize ourselves
4957 // here because currently there may be no unregistered that are inaccessible
4958 // (this state combination is not supported). Note releasing the caller and
4959 // leaving the lock before calling uninit()
4960 alock.release();
4961 autoCaller.release();
4962
4963 uninit();
4964
4965 mParent->unregisterMachine(this, id);
4966 // calls VirtualBox::saveSettings()
4967
4968 return S_OK;
4969 }
4970
4971 HRESULT rc = S_OK;
4972
4973 // discard saved state
4974 if (mData->mMachineState == MachineState_Saved)
4975 {
4976 // add the saved state file to the list of files the caller should delete
4977 Assert(!mSSData->strStateFilePath.isEmpty());
4978 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4979
4980 mSSData->strStateFilePath.setNull();
4981
4982 // unconditionally set the machine state to powered off, we now
4983 // know no session has locked the machine
4984 mData->mMachineState = MachineState_PoweredOff;
4985 }
4986
4987 size_t cSnapshots = 0;
4988 if (mData->mFirstSnapshot)
4989 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4990 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4991 // fail now before we start detaching media
4992 return setError(VBOX_E_INVALID_OBJECT_STATE,
4993 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4994 mUserData->s.strName.c_str(), cSnapshots);
4995
4996 // This list collects the medium objects from all medium attachments
4997 // which we will detach from the machine and its snapshots, in a specific
4998 // order which allows for closing all media without getting "media in use"
4999 // errors, simply by going through the list from the front to the back:
5000 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5001 // and must be closed before the parent media from the snapshots, or closing the parents
5002 // will fail because they still have children);
5003 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5004 // the root ("first") snapshot of the machine.
5005 MediaList llMedia;
5006
5007 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5008 && mMediaData->mAttachments.size()
5009 )
5010 {
5011 // we have media attachments: detach them all and add the Medium objects to our list
5012 if (cleanupMode != CleanupMode_UnregisterOnly)
5013 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5014 else
5015 return setError(VBOX_E_INVALID_OBJECT_STATE,
5016 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5017 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5018 }
5019
5020 if (cSnapshots)
5021 {
5022 // autoCleanup must be true here, or we would have failed above
5023
5024 // add the media from the medium attachments of the snapshots to llMedia
5025 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5026 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5027 // into the children first
5028
5029 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5030 MachineState_T oldState = mData->mMachineState;
5031 mData->mMachineState = MachineState_DeletingSnapshot;
5032
5033 // make a copy of the first snapshot so the refcount does not drop to 0
5034 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5035 // because of the AutoCaller voodoo)
5036 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5037
5038 // GO!
5039 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5040
5041 mData->mMachineState = oldState;
5042 }
5043
5044 if (FAILED(rc))
5045 {
5046 rollbackMedia();
5047 return rc;
5048 }
5049
5050 // commit all the media changes made above
5051 commitMedia();
5052
5053 mData->mRegistered = false;
5054
5055 // machine lock no longer needed
5056 alock.release();
5057
5058 // return media to caller
5059 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5060 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5061
5062 mParent->unregisterMachine(this, id);
5063 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5064
5065 return S_OK;
5066}
5067
5068struct Machine::DeleteTask
5069{
5070 ComObjPtr<Machine> pMachine;
5071 RTCList<ComPtr<IMedium> > llMediums;
5072 StringsList llFilesToDelete;
5073 ComObjPtr<Progress> pProgress;
5074};
5075
5076STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5077{
5078 LogFlowFuncEnter();
5079
5080 AutoCaller autoCaller(this);
5081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5082
5083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5084
5085 HRESULT rc = checkStateDependency(MutableStateDep);
5086 if (FAILED(rc)) return rc;
5087
5088 if (mData->mRegistered)
5089 return setError(VBOX_E_INVALID_VM_STATE,
5090 tr("Cannot delete settings of a registered machine"));
5091
5092 DeleteTask *pTask = new DeleteTask;
5093 pTask->pMachine = this;
5094 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5095
5096 // collect files to delete
5097 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5098
5099 for (size_t i = 0; i < sfaMedia.size(); ++i)
5100 {
5101 IMedium *pIMedium(sfaMedia[i]);
5102 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5103 if (pMedium.isNull())
5104 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5105 SafeArray<BSTR> ids;
5106 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5107 if (FAILED(rc)) return rc;
5108 /* At this point the medium should not have any back references
5109 * anymore. If it has it is attached to another VM and *must* not
5110 * deleted. */
5111 if (ids.size() < 1)
5112 pTask->llMediums.append(pMedium);
5113 }
5114 if (mData->pMachineConfigFile->fileExists())
5115 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5116
5117 pTask->pProgress.createObject();
5118 pTask->pProgress->init(getVirtualBox(),
5119 static_cast<IMachine*>(this) /* aInitiator */,
5120 Bstr(tr("Deleting files")).raw(),
5121 true /* fCancellable */,
5122 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5123 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5124
5125 int vrc = RTThreadCreate(NULL,
5126 Machine::deleteThread,
5127 (void*)pTask,
5128 0,
5129 RTTHREADTYPE_MAIN_WORKER,
5130 0,
5131 "MachineDelete");
5132
5133 pTask->pProgress.queryInterfaceTo(aProgress);
5134
5135 if (RT_FAILURE(vrc))
5136 {
5137 delete pTask;
5138 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5139 }
5140
5141 LogFlowFuncLeave();
5142
5143 return S_OK;
5144}
5145
5146/**
5147 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5148 * calls Machine::deleteTaskWorker() on the actual machine object.
5149 * @param Thread
5150 * @param pvUser
5151 * @return
5152 */
5153/*static*/
5154DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5155{
5156 LogFlowFuncEnter();
5157
5158 DeleteTask *pTask = (DeleteTask*)pvUser;
5159 Assert(pTask);
5160 Assert(pTask->pMachine);
5161 Assert(pTask->pProgress);
5162
5163 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5164 pTask->pProgress->notifyComplete(rc);
5165
5166 delete pTask;
5167
5168 LogFlowFuncLeave();
5169
5170 NOREF(Thread);
5171
5172 return VINF_SUCCESS;
5173}
5174
5175/**
5176 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5177 * @param task
5178 * @return
5179 */
5180HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5181{
5182 AutoCaller autoCaller(this);
5183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5184
5185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5186
5187 HRESULT rc = S_OK;
5188
5189 try
5190 {
5191 ULONG uLogHistoryCount = 3;
5192 ComPtr<ISystemProperties> systemProperties;
5193 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5194 if (FAILED(rc)) throw rc;
5195
5196 if (!systemProperties.isNull())
5197 {
5198 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5199 if (FAILED(rc)) throw rc;
5200 }
5201
5202 MachineState_T oldState = mData->mMachineState;
5203 setMachineState(MachineState_SettingUp);
5204 alock.release();
5205 for (size_t i = 0; i < task.llMediums.size(); ++i)
5206 {
5207 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5208 {
5209 AutoCaller mac(pMedium);
5210 if (FAILED(mac.rc())) throw mac.rc();
5211 Utf8Str strLocation = pMedium->getLocationFull();
5212 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5213 if (FAILED(rc)) throw rc;
5214 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5215 }
5216 ComPtr<IProgress> pProgress2;
5217 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5218 if (FAILED(rc)) throw rc;
5219 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5220 if (FAILED(rc)) throw rc;
5221 /* Check the result of the asynchrony process. */
5222 LONG iRc;
5223 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5224 if (FAILED(rc)) throw rc;
5225 /* If the thread of the progress object has an error, then
5226 * retrieve the error info from there, or it'll be lost. */
5227 if (FAILED(iRc))
5228 throw setError(ProgressErrorInfo(pProgress2));
5229 }
5230 setMachineState(oldState);
5231 alock.acquire();
5232
5233 // delete the files pushed on the task list by Machine::Delete()
5234 // (this includes saved states of the machine and snapshots and
5235 // medium storage files from the IMedium list passed in, and the
5236 // machine XML file)
5237 StringsList::const_iterator it = task.llFilesToDelete.begin();
5238 while (it != task.llFilesToDelete.end())
5239 {
5240 const Utf8Str &strFile = *it;
5241 LogFunc(("Deleting file %s\n", strFile.c_str()));
5242 int vrc = RTFileDelete(strFile.c_str());
5243 if (RT_FAILURE(vrc))
5244 throw setError(VBOX_E_IPRT_ERROR,
5245 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5246
5247 ++it;
5248 if (it == task.llFilesToDelete.end())
5249 {
5250 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5251 if (FAILED(rc)) throw rc;
5252 break;
5253 }
5254
5255 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5256 if (FAILED(rc)) throw rc;
5257 }
5258
5259 /* delete the settings only when the file actually exists */
5260 if (mData->pMachineConfigFile->fileExists())
5261 {
5262 /* Delete any backup or uncommitted XML files. Ignore failures.
5263 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5264 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5265 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5266 RTFileDelete(otherXml.c_str());
5267 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5268 RTFileDelete(otherXml.c_str());
5269
5270 /* delete the Logs folder, nothing important should be left
5271 * there (we don't check for errors because the user might have
5272 * some private files there that we don't want to delete) */
5273 Utf8Str logFolder;
5274 getLogFolder(logFolder);
5275 Assert(logFolder.length());
5276 if (RTDirExists(logFolder.c_str()))
5277 {
5278 /* Delete all VBox.log[.N] files from the Logs folder
5279 * (this must be in sync with the rotation logic in
5280 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5281 * files that may have been created by the GUI. */
5282 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5283 logFolder.c_str(), RTPATH_DELIMITER);
5284 RTFileDelete(log.c_str());
5285 log = Utf8StrFmt("%s%cVBox.png",
5286 logFolder.c_str(), RTPATH_DELIMITER);
5287 RTFileDelete(log.c_str());
5288 for (int i = uLogHistoryCount; i > 0; i--)
5289 {
5290 log = Utf8StrFmt("%s%cVBox.log.%d",
5291 logFolder.c_str(), RTPATH_DELIMITER, i);
5292 RTFileDelete(log.c_str());
5293 log = Utf8StrFmt("%s%cVBox.png.%d",
5294 logFolder.c_str(), RTPATH_DELIMITER, i);
5295 RTFileDelete(log.c_str());
5296 }
5297
5298 RTDirRemove(logFolder.c_str());
5299 }
5300
5301 /* delete the Snapshots folder, nothing important should be left
5302 * there (we don't check for errors because the user might have
5303 * some private files there that we don't want to delete) */
5304 Utf8Str strFullSnapshotFolder;
5305 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5306 Assert(!strFullSnapshotFolder.isEmpty());
5307 if (RTDirExists(strFullSnapshotFolder.c_str()))
5308 RTDirRemove(strFullSnapshotFolder.c_str());
5309
5310 // delete the directory that contains the settings file, but only
5311 // if it matches the VM name
5312 Utf8Str settingsDir;
5313 if (isInOwnDir(&settingsDir))
5314 RTDirRemove(settingsDir.c_str());
5315 }
5316
5317 alock.release();
5318
5319 mParent->saveModifiedRegistries();
5320 }
5321 catch (HRESULT aRC) { rc = aRC; }
5322
5323 return rc;
5324}
5325
5326STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5327{
5328 CheckComArgOutPointerValid(aSnapshot);
5329
5330 AutoCaller autoCaller(this);
5331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5332
5333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5334
5335 ComObjPtr<Snapshot> pSnapshot;
5336 HRESULT rc;
5337
5338 if (!aNameOrId || !*aNameOrId)
5339 // null case (caller wants root snapshot): findSnapshotById() handles this
5340 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5341 else
5342 {
5343 Guid uuid(aNameOrId);
5344 if (uuid.isValid())
5345 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5346 else
5347 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5348 }
5349 pSnapshot.queryInterfaceTo(aSnapshot);
5350
5351 return rc;
5352}
5353
5354STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5355{
5356 CheckComArgStrNotEmptyOrNull(aName);
5357 CheckComArgStrNotEmptyOrNull(aHostPath);
5358
5359 AutoCaller autoCaller(this);
5360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5361
5362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5363
5364 HRESULT rc = checkStateDependency(MutableStateDep);
5365 if (FAILED(rc)) return rc;
5366
5367 Utf8Str strName(aName);
5368
5369 ComObjPtr<SharedFolder> sharedFolder;
5370 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5371 if (SUCCEEDED(rc))
5372 return setError(VBOX_E_OBJECT_IN_USE,
5373 tr("Shared folder named '%s' already exists"),
5374 strName.c_str());
5375
5376 sharedFolder.createObject();
5377 rc = sharedFolder->init(getMachine(),
5378 strName,
5379 aHostPath,
5380 !!aWritable,
5381 !!aAutoMount,
5382 true /* fFailOnError */);
5383 if (FAILED(rc)) return rc;
5384
5385 setModified(IsModified_SharedFolders);
5386 mHWData.backup();
5387 mHWData->mSharedFolders.push_back(sharedFolder);
5388
5389 /* inform the direct session if any */
5390 alock.release();
5391 onSharedFolderChange();
5392
5393 return S_OK;
5394}
5395
5396STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5397{
5398 CheckComArgStrNotEmptyOrNull(aName);
5399
5400 AutoCaller autoCaller(this);
5401 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5402
5403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5404
5405 HRESULT rc = checkStateDependency(MutableStateDep);
5406 if (FAILED(rc)) return rc;
5407
5408 ComObjPtr<SharedFolder> sharedFolder;
5409 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5410 if (FAILED(rc)) return rc;
5411
5412 setModified(IsModified_SharedFolders);
5413 mHWData.backup();
5414 mHWData->mSharedFolders.remove(sharedFolder);
5415
5416 /* inform the direct session if any */
5417 alock.release();
5418 onSharedFolderChange();
5419
5420 return S_OK;
5421}
5422
5423STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5424{
5425 CheckComArgOutPointerValid(aCanShow);
5426
5427 /* start with No */
5428 *aCanShow = FALSE;
5429
5430 AutoCaller autoCaller(this);
5431 AssertComRCReturnRC(autoCaller.rc());
5432
5433 ComPtr<IInternalSessionControl> directControl;
5434 {
5435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5436
5437 if (mData->mSession.mState != SessionState_Locked)
5438 return setError(VBOX_E_INVALID_VM_STATE,
5439 tr("Machine is not locked for session (session state: %s)"),
5440 Global::stringifySessionState(mData->mSession.mState));
5441
5442 directControl = mData->mSession.mDirectControl;
5443 }
5444
5445 /* ignore calls made after #OnSessionEnd() is called */
5446 if (!directControl)
5447 return S_OK;
5448
5449 LONG64 dummy;
5450 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5451}
5452
5453STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5454{
5455 CheckComArgOutPointerValid(aWinId);
5456
5457 AutoCaller autoCaller(this);
5458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5459
5460 ComPtr<IInternalSessionControl> directControl;
5461 {
5462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5463
5464 if (mData->mSession.mState != SessionState_Locked)
5465 return setError(E_FAIL,
5466 tr("Machine is not locked for session (session state: %s)"),
5467 Global::stringifySessionState(mData->mSession.mState));
5468
5469 directControl = mData->mSession.mDirectControl;
5470 }
5471
5472 /* ignore calls made after #OnSessionEnd() is called */
5473 if (!directControl)
5474 return S_OK;
5475
5476 BOOL dummy;
5477 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5478}
5479
5480#ifdef VBOX_WITH_GUEST_PROPS
5481/**
5482 * Look up a guest property in VBoxSVC's internal structures.
5483 */
5484HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5485 BSTR *aValue,
5486 LONG64 *aTimestamp,
5487 BSTR *aFlags) const
5488{
5489 using namespace guestProp;
5490
5491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5492 Utf8Str strName(aName);
5493 HWData::GuestPropertyMap::const_iterator it =
5494 mHWData->mGuestProperties.find(strName);
5495
5496 if (it != mHWData->mGuestProperties.end())
5497 {
5498 char szFlags[MAX_FLAGS_LEN + 1];
5499 it->second.strValue.cloneTo(aValue);
5500 *aTimestamp = it->second.mTimestamp;
5501 writeFlags(it->second.mFlags, szFlags);
5502 Bstr(szFlags).cloneTo(aFlags);
5503 }
5504
5505 return S_OK;
5506}
5507
5508/**
5509 * Query the VM that a guest property belongs to for the property.
5510 * @returns E_ACCESSDENIED if the VM process is not available or not
5511 * currently handling queries and the lookup should then be done in
5512 * VBoxSVC.
5513 */
5514HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5515 BSTR *aValue,
5516 LONG64 *aTimestamp,
5517 BSTR *aFlags) const
5518{
5519 HRESULT rc;
5520 ComPtr<IInternalSessionControl> directControl;
5521 directControl = mData->mSession.mDirectControl;
5522
5523 /* fail if we were called after #OnSessionEnd() is called. This is a
5524 * silly race condition. */
5525
5526 if (!directControl)
5527 rc = E_ACCESSDENIED;
5528 else
5529 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5530 false /* isSetter */,
5531 aValue, aTimestamp, aFlags);
5532 return rc;
5533}
5534#endif // VBOX_WITH_GUEST_PROPS
5535
5536STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5537 BSTR *aValue,
5538 LONG64 *aTimestamp,
5539 BSTR *aFlags)
5540{
5541#ifndef VBOX_WITH_GUEST_PROPS
5542 ReturnComNotImplemented();
5543#else // VBOX_WITH_GUEST_PROPS
5544 CheckComArgStrNotEmptyOrNull(aName);
5545 CheckComArgOutPointerValid(aValue);
5546 CheckComArgOutPointerValid(aTimestamp);
5547 CheckComArgOutPointerValid(aFlags);
5548
5549 AutoCaller autoCaller(this);
5550 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5551
5552 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5553 if (rc == E_ACCESSDENIED)
5554 /* The VM is not running or the service is not (yet) accessible */
5555 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5556 return rc;
5557#endif // VBOX_WITH_GUEST_PROPS
5558}
5559
5560STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5561{
5562 LONG64 dummyTimestamp;
5563 Bstr dummyFlags;
5564 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5565}
5566
5567STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5568{
5569 Bstr dummyValue;
5570 Bstr dummyFlags;
5571 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5572}
5573
5574#ifdef VBOX_WITH_GUEST_PROPS
5575/**
5576 * Set a guest property in VBoxSVC's internal structures.
5577 */
5578HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5579 IN_BSTR aFlags)
5580{
5581 using namespace guestProp;
5582
5583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5584 HRESULT rc = S_OK;
5585 HWData::GuestProperty property;
5586 property.mFlags = NILFLAG;
5587
5588 rc = checkStateDependency(MutableStateDep);
5589 if (FAILED(rc)) return rc;
5590
5591 try
5592 {
5593 Utf8Str utf8Name(aName);
5594 Utf8Str utf8Flags(aFlags);
5595 uint32_t fFlags = NILFLAG;
5596 if ( (aFlags != NULL)
5597 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5598 )
5599 return setError(E_INVALIDARG,
5600 tr("Invalid guest property flag values: '%ls'"),
5601 aFlags);
5602
5603 HWData::GuestPropertyMap::iterator it =
5604 mHWData->mGuestProperties.find(utf8Name);
5605
5606 if (it == mHWData->mGuestProperties.end())
5607 {
5608 setModified(IsModified_MachineData);
5609 mHWData.backupEx();
5610
5611 RTTIMESPEC time;
5612 HWData::GuestProperty prop;
5613 prop.strValue = aValue;
5614 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5615 prop.mFlags = fFlags;
5616
5617 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5618 }
5619 else
5620 {
5621 if (it->second.mFlags & (RDONLYHOST))
5622 {
5623 rc = setError(E_ACCESSDENIED,
5624 tr("The property '%ls' cannot be changed by the host"),
5625 aName);
5626 }
5627 else
5628 {
5629 setModified(IsModified_MachineData);
5630 mHWData.backupEx();
5631
5632 /* The backupEx() operation invalidates our iterator,
5633 * so get a new one. */
5634 it = mHWData->mGuestProperties.find(utf8Name);
5635 Assert(it != mHWData->mGuestProperties.end());
5636
5637 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5638 {
5639 RTTIMESPEC time;
5640 it->second.strValue = aValue;
5641 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5642 if (aFlags != NULL)
5643 it->second.mFlags = fFlags;
5644 }
5645 else
5646 {
5647 mHWData->mGuestProperties.erase(it);
5648 }
5649 }
5650 }
5651
5652 if ( SUCCEEDED(rc)
5653 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5654 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5655 RTSTR_MAX,
5656 utf8Name.c_str(),
5657 RTSTR_MAX,
5658 NULL)
5659 )
5660 )
5661 {
5662 alock.release();
5663
5664 mParent->onGuestPropertyChange(mData->mUuid, aName,
5665 aValue ? aValue : Bstr("").raw(),
5666 aFlags ? aFlags : Bstr("").raw());
5667 }
5668 }
5669 catch (std::bad_alloc &)
5670 {
5671 rc = E_OUTOFMEMORY;
5672 }
5673
5674 return rc;
5675}
5676
5677/**
5678 * Set a property on the VM that that property belongs to.
5679 * @returns E_ACCESSDENIED if the VM process is not available or not
5680 * currently handling queries and the setting should then be done in
5681 * VBoxSVC.
5682 */
5683HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5684 IN_BSTR aFlags)
5685{
5686 HRESULT rc;
5687
5688 try
5689 {
5690 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5691
5692 BSTR dummy = NULL; /* will not be changed (setter) */
5693 LONG64 dummy64;
5694 if (!directControl)
5695 rc = E_ACCESSDENIED;
5696 else
5697 /** @todo Fix when adding DeleteGuestProperty(),
5698 see defect. */
5699 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5700 true /* isSetter */,
5701 &dummy, &dummy64, &dummy);
5702 }
5703 catch (std::bad_alloc &)
5704 {
5705 rc = E_OUTOFMEMORY;
5706 }
5707
5708 return rc;
5709}
5710#endif // VBOX_WITH_GUEST_PROPS
5711
5712STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5713 IN_BSTR aFlags)
5714{
5715#ifndef VBOX_WITH_GUEST_PROPS
5716 ReturnComNotImplemented();
5717#else // VBOX_WITH_GUEST_PROPS
5718 CheckComArgStrNotEmptyOrNull(aName);
5719 CheckComArgMaybeNull(aFlags);
5720 CheckComArgMaybeNull(aValue);
5721
5722 AutoCaller autoCaller(this);
5723 if (FAILED(autoCaller.rc()))
5724 return autoCaller.rc();
5725
5726 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5727 if (rc == E_ACCESSDENIED)
5728 /* The VM is not running or the service is not (yet) accessible */
5729 rc = setGuestPropertyToService(aName, aValue, aFlags);
5730 return rc;
5731#endif // VBOX_WITH_GUEST_PROPS
5732}
5733
5734STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5735{
5736 return SetGuestProperty(aName, aValue, NULL);
5737}
5738
5739STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5740{
5741 return SetGuestProperty(aName, NULL, NULL);
5742}
5743
5744#ifdef VBOX_WITH_GUEST_PROPS
5745/**
5746 * Enumerate the guest properties in VBoxSVC's internal structures.
5747 */
5748HRESULT Machine::enumerateGuestPropertiesInService
5749 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5750 ComSafeArrayOut(BSTR, aValues),
5751 ComSafeArrayOut(LONG64, aTimestamps),
5752 ComSafeArrayOut(BSTR, aFlags))
5753{
5754 using namespace guestProp;
5755
5756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5757 Utf8Str strPatterns(aPatterns);
5758
5759 HWData::GuestPropertyMap propMap;
5760
5761 /*
5762 * Look for matching patterns and build up a list.
5763 */
5764 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5765 while (it != mHWData->mGuestProperties.end())
5766 {
5767 if ( strPatterns.isEmpty()
5768 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5769 RTSTR_MAX,
5770 it->first.c_str(),
5771 RTSTR_MAX,
5772 NULL)
5773 )
5774 {
5775 propMap.insert(*it);
5776 }
5777
5778 it++;
5779 }
5780
5781 alock.release();
5782
5783 /*
5784 * And build up the arrays for returning the property information.
5785 */
5786 size_t cEntries = propMap.size();
5787 SafeArray<BSTR> names(cEntries);
5788 SafeArray<BSTR> values(cEntries);
5789 SafeArray<LONG64> timestamps(cEntries);
5790 SafeArray<BSTR> flags(cEntries);
5791 size_t iProp = 0;
5792
5793 it = propMap.begin();
5794 while (it != propMap.end())
5795 {
5796 char szFlags[MAX_FLAGS_LEN + 1];
5797 it->first.cloneTo(&names[iProp]);
5798 it->second.strValue.cloneTo(&values[iProp]);
5799 timestamps[iProp] = it->second.mTimestamp;
5800 writeFlags(it->second.mFlags, szFlags);
5801 Bstr(szFlags).cloneTo(&flags[iProp++]);
5802 it++;
5803 }
5804 names.detachTo(ComSafeArrayOutArg(aNames));
5805 values.detachTo(ComSafeArrayOutArg(aValues));
5806 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5807 flags.detachTo(ComSafeArrayOutArg(aFlags));
5808 return S_OK;
5809}
5810
5811/**
5812 * Enumerate the properties managed by a VM.
5813 * @returns E_ACCESSDENIED if the VM process is not available or not
5814 * currently handling queries and the setting should then be done in
5815 * VBoxSVC.
5816 */
5817HRESULT Machine::enumerateGuestPropertiesOnVM
5818 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5819 ComSafeArrayOut(BSTR, aValues),
5820 ComSafeArrayOut(LONG64, aTimestamps),
5821 ComSafeArrayOut(BSTR, aFlags))
5822{
5823 HRESULT rc;
5824 ComPtr<IInternalSessionControl> directControl;
5825 directControl = mData->mSession.mDirectControl;
5826
5827 if (!directControl)
5828 rc = E_ACCESSDENIED;
5829 else
5830 rc = directControl->EnumerateGuestProperties
5831 (aPatterns, ComSafeArrayOutArg(aNames),
5832 ComSafeArrayOutArg(aValues),
5833 ComSafeArrayOutArg(aTimestamps),
5834 ComSafeArrayOutArg(aFlags));
5835 return rc;
5836}
5837#endif // VBOX_WITH_GUEST_PROPS
5838
5839STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5840 ComSafeArrayOut(BSTR, aNames),
5841 ComSafeArrayOut(BSTR, aValues),
5842 ComSafeArrayOut(LONG64, aTimestamps),
5843 ComSafeArrayOut(BSTR, aFlags))
5844{
5845#ifndef VBOX_WITH_GUEST_PROPS
5846 ReturnComNotImplemented();
5847#else // VBOX_WITH_GUEST_PROPS
5848 CheckComArgMaybeNull(aPatterns);
5849 CheckComArgOutSafeArrayPointerValid(aNames);
5850 CheckComArgOutSafeArrayPointerValid(aValues);
5851 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5852 CheckComArgOutSafeArrayPointerValid(aFlags);
5853
5854 AutoCaller autoCaller(this);
5855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5856
5857 HRESULT rc = enumerateGuestPropertiesOnVM
5858 (aPatterns, ComSafeArrayOutArg(aNames),
5859 ComSafeArrayOutArg(aValues),
5860 ComSafeArrayOutArg(aTimestamps),
5861 ComSafeArrayOutArg(aFlags));
5862 if (rc == E_ACCESSDENIED)
5863 /* The VM is not running or the service is not (yet) accessible */
5864 rc = enumerateGuestPropertiesInService
5865 (aPatterns, ComSafeArrayOutArg(aNames),
5866 ComSafeArrayOutArg(aValues),
5867 ComSafeArrayOutArg(aTimestamps),
5868 ComSafeArrayOutArg(aFlags));
5869 return rc;
5870#endif // VBOX_WITH_GUEST_PROPS
5871}
5872
5873STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5874 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5875{
5876 MediaData::AttachmentList atts;
5877
5878 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5879 if (FAILED(rc)) return rc;
5880
5881 SafeIfaceArray<IMediumAttachment> attachments(atts);
5882 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5883
5884 return S_OK;
5885}
5886
5887STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5888 LONG aControllerPort,
5889 LONG aDevice,
5890 IMediumAttachment **aAttachment)
5891{
5892 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5893 aControllerName, aControllerPort, aDevice));
5894
5895 CheckComArgStrNotEmptyOrNull(aControllerName);
5896 CheckComArgOutPointerValid(aAttachment);
5897
5898 AutoCaller autoCaller(this);
5899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5900
5901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5902
5903 *aAttachment = NULL;
5904
5905 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5906 aControllerName,
5907 aControllerPort,
5908 aDevice);
5909 if (pAttach.isNull())
5910 return setError(VBOX_E_OBJECT_NOT_FOUND,
5911 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5912 aDevice, aControllerPort, aControllerName);
5913
5914 pAttach.queryInterfaceTo(aAttachment);
5915
5916 return S_OK;
5917}
5918
5919STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5920 StorageBus_T aConnectionType,
5921 IStorageController **controller)
5922{
5923 CheckComArgStrNotEmptyOrNull(aName);
5924
5925 if ( (aConnectionType <= StorageBus_Null)
5926 || (aConnectionType > StorageBus_SAS))
5927 return setError(E_INVALIDARG,
5928 tr("Invalid connection type: %d"),
5929 aConnectionType);
5930
5931 AutoCaller autoCaller(this);
5932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5933
5934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5935
5936 HRESULT rc = checkStateDependency(MutableStateDep);
5937 if (FAILED(rc)) return rc;
5938
5939 /* try to find one with the name first. */
5940 ComObjPtr<StorageController> ctrl;
5941
5942 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5943 if (SUCCEEDED(rc))
5944 return setError(VBOX_E_OBJECT_IN_USE,
5945 tr("Storage controller named '%ls' already exists"),
5946 aName);
5947
5948 ctrl.createObject();
5949
5950 /* get a new instance number for the storage controller */
5951 ULONG ulInstance = 0;
5952 bool fBootable = true;
5953 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5954 it != mStorageControllers->end();
5955 ++it)
5956 {
5957 if ((*it)->getStorageBus() == aConnectionType)
5958 {
5959 ULONG ulCurInst = (*it)->getInstance();
5960
5961 if (ulCurInst >= ulInstance)
5962 ulInstance = ulCurInst + 1;
5963
5964 /* Only one controller of each type can be marked as bootable. */
5965 if ((*it)->getBootable())
5966 fBootable = false;
5967 }
5968 }
5969
5970 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5971 if (FAILED(rc)) return rc;
5972
5973 setModified(IsModified_Storage);
5974 mStorageControllers.backup();
5975 mStorageControllers->push_back(ctrl);
5976
5977 ctrl.queryInterfaceTo(controller);
5978
5979 /* inform the direct session if any */
5980 alock.release();
5981 onStorageControllerChange();
5982
5983 return S_OK;
5984}
5985
5986STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5987 IStorageController **aStorageController)
5988{
5989 CheckComArgStrNotEmptyOrNull(aName);
5990
5991 AutoCaller autoCaller(this);
5992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5993
5994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5995
5996 ComObjPtr<StorageController> ctrl;
5997
5998 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5999 if (SUCCEEDED(rc))
6000 ctrl.queryInterfaceTo(aStorageController);
6001
6002 return rc;
6003}
6004
6005STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6006 IStorageController **aStorageController)
6007{
6008 AutoCaller autoCaller(this);
6009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6010
6011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6012
6013 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6014 it != mStorageControllers->end();
6015 ++it)
6016 {
6017 if ((*it)->getInstance() == aInstance)
6018 {
6019 (*it).queryInterfaceTo(aStorageController);
6020 return S_OK;
6021 }
6022 }
6023
6024 return setError(VBOX_E_OBJECT_NOT_FOUND,
6025 tr("Could not find a storage controller with instance number '%lu'"),
6026 aInstance);
6027}
6028
6029STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6030{
6031 AutoCaller autoCaller(this);
6032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6033
6034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6035
6036 HRESULT rc = checkStateDependency(MutableStateDep);
6037 if (FAILED(rc)) return rc;
6038
6039 ComObjPtr<StorageController> ctrl;
6040
6041 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6042 if (SUCCEEDED(rc))
6043 {
6044 /* Ensure that only one controller of each type is marked as bootable. */
6045 if (fBootable == TRUE)
6046 {
6047 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6048 it != mStorageControllers->end();
6049 ++it)
6050 {
6051 ComObjPtr<StorageController> aCtrl = (*it);
6052
6053 if ( (aCtrl->getName() != Utf8Str(aName))
6054 && aCtrl->getBootable() == TRUE
6055 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6056 && aCtrl->getControllerType() == ctrl->getControllerType())
6057 {
6058 aCtrl->setBootable(FALSE);
6059 break;
6060 }
6061 }
6062 }
6063
6064 if (SUCCEEDED(rc))
6065 {
6066 ctrl->setBootable(fBootable);
6067 setModified(IsModified_Storage);
6068 }
6069 }
6070
6071 if (SUCCEEDED(rc))
6072 {
6073 /* inform the direct session if any */
6074 alock.release();
6075 onStorageControllerChange();
6076 }
6077
6078 return rc;
6079}
6080
6081STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6082{
6083 CheckComArgStrNotEmptyOrNull(aName);
6084
6085 AutoCaller autoCaller(this);
6086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6087
6088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6089
6090 HRESULT rc = checkStateDependency(MutableStateDep);
6091 if (FAILED(rc)) return rc;
6092
6093 ComObjPtr<StorageController> ctrl;
6094 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6095 if (FAILED(rc)) return rc;
6096
6097 {
6098 /* find all attached devices to the appropriate storage controller and detach them all */
6099 // make a temporary list because detachDevice invalidates iterators into
6100 // mMediaData->mAttachments
6101 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6102
6103 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6104 it != llAttachments2.end();
6105 ++it)
6106 {
6107 MediumAttachment *pAttachTemp = *it;
6108
6109 AutoCaller localAutoCaller(pAttachTemp);
6110 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6111
6112 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6113
6114 if (pAttachTemp->getControllerName() == aName)
6115 {
6116 rc = detachDevice(pAttachTemp, alock, NULL);
6117 if (FAILED(rc)) return rc;
6118 }
6119 }
6120 }
6121
6122 /* We can remove it now. */
6123 setModified(IsModified_Storage);
6124 mStorageControllers.backup();
6125
6126 ctrl->unshare();
6127
6128 mStorageControllers->remove(ctrl);
6129
6130 /* inform the direct session if any */
6131 alock.release();
6132 onStorageControllerChange();
6133
6134 return S_OK;
6135}
6136
6137STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6138 ULONG *puOriginX,
6139 ULONG *puOriginY,
6140 ULONG *puWidth,
6141 ULONG *puHeight,
6142 BOOL *pfEnabled)
6143{
6144 LogFlowThisFunc(("\n"));
6145
6146 CheckComArgNotNull(puOriginX);
6147 CheckComArgNotNull(puOriginY);
6148 CheckComArgNotNull(puWidth);
6149 CheckComArgNotNull(puHeight);
6150 CheckComArgNotNull(pfEnabled);
6151
6152 uint32_t u32OriginX= 0;
6153 uint32_t u32OriginY= 0;
6154 uint32_t u32Width = 0;
6155 uint32_t u32Height = 0;
6156 uint16_t u16Flags = 0;
6157
6158 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6159 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6160 if (RT_FAILURE(vrc))
6161 {
6162#ifdef RT_OS_WINDOWS
6163 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6164 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6165 * So just assign fEnable to TRUE again.
6166 * The right fix would be to change GUI API wrappers to make sure that parameters
6167 * are changed only if API succeeds.
6168 */
6169 *pfEnabled = TRUE;
6170#endif
6171 return setError(VBOX_E_IPRT_ERROR,
6172 tr("Saved guest size is not available (%Rrc)"),
6173 vrc);
6174 }
6175
6176 *puOriginX = u32OriginX;
6177 *puOriginY = u32OriginY;
6178 *puWidth = u32Width;
6179 *puHeight = u32Height;
6180 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6181
6182 return S_OK;
6183}
6184
6185STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6186{
6187 LogFlowThisFunc(("\n"));
6188
6189 CheckComArgNotNull(aSize);
6190 CheckComArgNotNull(aWidth);
6191 CheckComArgNotNull(aHeight);
6192
6193 if (aScreenId != 0)
6194 return E_NOTIMPL;
6195
6196 AutoCaller autoCaller(this);
6197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6198
6199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6200
6201 uint8_t *pu8Data = NULL;
6202 uint32_t cbData = 0;
6203 uint32_t u32Width = 0;
6204 uint32_t u32Height = 0;
6205
6206 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6207
6208 if (RT_FAILURE(vrc))
6209 return setError(VBOX_E_IPRT_ERROR,
6210 tr("Saved screenshot data is not available (%Rrc)"),
6211 vrc);
6212
6213 *aSize = cbData;
6214 *aWidth = u32Width;
6215 *aHeight = u32Height;
6216
6217 freeSavedDisplayScreenshot(pu8Data);
6218
6219 return S_OK;
6220}
6221
6222STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6223{
6224 LogFlowThisFunc(("\n"));
6225
6226 CheckComArgNotNull(aWidth);
6227 CheckComArgNotNull(aHeight);
6228 CheckComArgOutSafeArrayPointerValid(aData);
6229
6230 if (aScreenId != 0)
6231 return E_NOTIMPL;
6232
6233 AutoCaller autoCaller(this);
6234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6235
6236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6237
6238 uint8_t *pu8Data = NULL;
6239 uint32_t cbData = 0;
6240 uint32_t u32Width = 0;
6241 uint32_t u32Height = 0;
6242
6243 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6244
6245 if (RT_FAILURE(vrc))
6246 return setError(VBOX_E_IPRT_ERROR,
6247 tr("Saved screenshot data is not available (%Rrc)"),
6248 vrc);
6249
6250 *aWidth = u32Width;
6251 *aHeight = u32Height;
6252
6253 com::SafeArray<BYTE> bitmap(cbData);
6254 /* Convert pixels to format expected by the API caller. */
6255 if (aBGR)
6256 {
6257 /* [0] B, [1] G, [2] R, [3] A. */
6258 for (unsigned i = 0; i < cbData; i += 4)
6259 {
6260 bitmap[i] = pu8Data[i];
6261 bitmap[i + 1] = pu8Data[i + 1];
6262 bitmap[i + 2] = pu8Data[i + 2];
6263 bitmap[i + 3] = 0xff;
6264 }
6265 }
6266 else
6267 {
6268 /* [0] R, [1] G, [2] B, [3] A. */
6269 for (unsigned i = 0; i < cbData; i += 4)
6270 {
6271 bitmap[i] = pu8Data[i + 2];
6272 bitmap[i + 1] = pu8Data[i + 1];
6273 bitmap[i + 2] = pu8Data[i];
6274 bitmap[i + 3] = 0xff;
6275 }
6276 }
6277 bitmap.detachTo(ComSafeArrayOutArg(aData));
6278
6279 freeSavedDisplayScreenshot(pu8Data);
6280
6281 return S_OK;
6282}
6283
6284
6285STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6286{
6287 LogFlowThisFunc(("\n"));
6288
6289 CheckComArgNotNull(aWidth);
6290 CheckComArgNotNull(aHeight);
6291 CheckComArgOutSafeArrayPointerValid(aData);
6292
6293 if (aScreenId != 0)
6294 return E_NOTIMPL;
6295
6296 AutoCaller autoCaller(this);
6297 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6298
6299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6300
6301 uint8_t *pu8Data = NULL;
6302 uint32_t cbData = 0;
6303 uint32_t u32Width = 0;
6304 uint32_t u32Height = 0;
6305
6306 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6307
6308 if (RT_FAILURE(vrc))
6309 return setError(VBOX_E_IPRT_ERROR,
6310 tr("Saved screenshot data is not available (%Rrc)"),
6311 vrc);
6312
6313 *aWidth = u32Width;
6314 *aHeight = u32Height;
6315
6316 HRESULT rc = S_OK;
6317 uint8_t *pu8PNG = NULL;
6318 uint32_t cbPNG = 0;
6319 uint32_t cxPNG = 0;
6320 uint32_t cyPNG = 0;
6321
6322 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6323
6324 if (RT_SUCCESS(vrc))
6325 {
6326 com::SafeArray<BYTE> screenData(cbPNG);
6327 screenData.initFrom(pu8PNG, cbPNG);
6328 if (pu8PNG)
6329 RTMemFree(pu8PNG);
6330 screenData.detachTo(ComSafeArrayOutArg(aData));
6331 }
6332 else
6333 {
6334 if (pu8PNG)
6335 RTMemFree(pu8PNG);
6336 return setError(VBOX_E_IPRT_ERROR,
6337 tr("Could not convert screenshot to PNG (%Rrc)"),
6338 vrc);
6339 }
6340
6341 freeSavedDisplayScreenshot(pu8Data);
6342
6343 return rc;
6344}
6345
6346STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6347{
6348 LogFlowThisFunc(("\n"));
6349
6350 CheckComArgNotNull(aSize);
6351 CheckComArgNotNull(aWidth);
6352 CheckComArgNotNull(aHeight);
6353
6354 if (aScreenId != 0)
6355 return E_NOTIMPL;
6356
6357 AutoCaller autoCaller(this);
6358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6359
6360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6361
6362 uint8_t *pu8Data = NULL;
6363 uint32_t cbData = 0;
6364 uint32_t u32Width = 0;
6365 uint32_t u32Height = 0;
6366
6367 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6368
6369 if (RT_FAILURE(vrc))
6370 return setError(VBOX_E_IPRT_ERROR,
6371 tr("Saved screenshot data is not available (%Rrc)"),
6372 vrc);
6373
6374 *aSize = cbData;
6375 *aWidth = u32Width;
6376 *aHeight = u32Height;
6377
6378 freeSavedDisplayScreenshot(pu8Data);
6379
6380 return S_OK;
6381}
6382
6383STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6384{
6385 LogFlowThisFunc(("\n"));
6386
6387 CheckComArgNotNull(aWidth);
6388 CheckComArgNotNull(aHeight);
6389 CheckComArgOutSafeArrayPointerValid(aData);
6390
6391 if (aScreenId != 0)
6392 return E_NOTIMPL;
6393
6394 AutoCaller autoCaller(this);
6395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6396
6397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6398
6399 uint8_t *pu8Data = NULL;
6400 uint32_t cbData = 0;
6401 uint32_t u32Width = 0;
6402 uint32_t u32Height = 0;
6403
6404 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6405
6406 if (RT_FAILURE(vrc))
6407 return setError(VBOX_E_IPRT_ERROR,
6408 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6409 vrc);
6410
6411 *aWidth = u32Width;
6412 *aHeight = u32Height;
6413
6414 com::SafeArray<BYTE> png(cbData);
6415 png.initFrom(pu8Data, cbData);
6416 png.detachTo(ComSafeArrayOutArg(aData));
6417
6418 freeSavedDisplayScreenshot(pu8Data);
6419
6420 return S_OK;
6421}
6422
6423STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6424{
6425 HRESULT rc = S_OK;
6426 LogFlowThisFunc(("\n"));
6427
6428 AutoCaller autoCaller(this);
6429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6430
6431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6432
6433 if (!mHWData->mCPUHotPlugEnabled)
6434 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6435
6436 if (aCpu >= mHWData->mCPUCount)
6437 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6438
6439 if (mHWData->mCPUAttached[aCpu])
6440 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6441
6442 alock.release();
6443 rc = onCPUChange(aCpu, false);
6444 alock.acquire();
6445 if (FAILED(rc)) return rc;
6446
6447 setModified(IsModified_MachineData);
6448 mHWData.backup();
6449 mHWData->mCPUAttached[aCpu] = true;
6450
6451 /* Save settings if online */
6452 if (Global::IsOnline(mData->mMachineState))
6453 saveSettings(NULL);
6454
6455 return S_OK;
6456}
6457
6458STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6459{
6460 HRESULT rc = S_OK;
6461 LogFlowThisFunc(("\n"));
6462
6463 AutoCaller autoCaller(this);
6464 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6465
6466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6467
6468 if (!mHWData->mCPUHotPlugEnabled)
6469 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6470
6471 if (aCpu >= SchemaDefs::MaxCPUCount)
6472 return setError(E_INVALIDARG,
6473 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6474 SchemaDefs::MaxCPUCount);
6475
6476 if (!mHWData->mCPUAttached[aCpu])
6477 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6478
6479 /* CPU 0 can't be detached */
6480 if (aCpu == 0)
6481 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6482
6483 alock.release();
6484 rc = onCPUChange(aCpu, true);
6485 alock.acquire();
6486 if (FAILED(rc)) return rc;
6487
6488 setModified(IsModified_MachineData);
6489 mHWData.backup();
6490 mHWData->mCPUAttached[aCpu] = false;
6491
6492 /* Save settings if online */
6493 if (Global::IsOnline(mData->mMachineState))
6494 saveSettings(NULL);
6495
6496 return S_OK;
6497}
6498
6499STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6500{
6501 LogFlowThisFunc(("\n"));
6502
6503 CheckComArgNotNull(aCpuAttached);
6504
6505 *aCpuAttached = false;
6506
6507 AutoCaller autoCaller(this);
6508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6509
6510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 /* If hotplug is enabled the CPU is always enabled. */
6513 if (!mHWData->mCPUHotPlugEnabled)
6514 {
6515 if (aCpu < mHWData->mCPUCount)
6516 *aCpuAttached = true;
6517 }
6518 else
6519 {
6520 if (aCpu < SchemaDefs::MaxCPUCount)
6521 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6522 }
6523
6524 return S_OK;
6525}
6526
6527STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6528{
6529 CheckComArgOutPointerValid(aName);
6530
6531 AutoCaller autoCaller(this);
6532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6533
6534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6535
6536 Utf8Str log = queryLogFilename(aIdx);
6537 if (!RTFileExists(log.c_str()))
6538 log.setNull();
6539 log.cloneTo(aName);
6540
6541 return S_OK;
6542}
6543
6544STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6545{
6546 LogFlowThisFunc(("\n"));
6547 CheckComArgOutSafeArrayPointerValid(aData);
6548 if (aSize < 0)
6549 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6550
6551 AutoCaller autoCaller(this);
6552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6553
6554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 HRESULT rc = S_OK;
6557 Utf8Str log = queryLogFilename(aIdx);
6558
6559 /* do not unnecessarily hold the lock while doing something which does
6560 * not need the lock and potentially takes a long time. */
6561 alock.release();
6562
6563 /* Limit the chunk size to 32K for now, as that gives better performance
6564 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6565 * One byte expands to approx. 25 bytes of breathtaking XML. */
6566 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6567 com::SafeArray<BYTE> logData(cbData);
6568
6569 RTFILE LogFile;
6570 int vrc = RTFileOpen(&LogFile, log.c_str(),
6571 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6572 if (RT_SUCCESS(vrc))
6573 {
6574 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6575 if (RT_SUCCESS(vrc))
6576 logData.resize(cbData);
6577 else
6578 rc = setError(VBOX_E_IPRT_ERROR,
6579 tr("Could not read log file '%s' (%Rrc)"),
6580 log.c_str(), vrc);
6581 RTFileClose(LogFile);
6582 }
6583 else
6584 rc = setError(VBOX_E_IPRT_ERROR,
6585 tr("Could not open log file '%s' (%Rrc)"),
6586 log.c_str(), vrc);
6587
6588 if (FAILED(rc))
6589 logData.resize(0);
6590 logData.detachTo(ComSafeArrayOutArg(aData));
6591
6592 return rc;
6593}
6594
6595
6596/**
6597 * Currently this method doesn't attach device to the running VM,
6598 * just makes sure it's plugged on next VM start.
6599 */
6600STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6601{
6602 AutoCaller autoCaller(this);
6603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6604
6605 // lock scope
6606 {
6607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6608
6609 HRESULT rc = checkStateDependency(MutableStateDep);
6610 if (FAILED(rc)) return rc;
6611
6612 ChipsetType_T aChipset = ChipsetType_PIIX3;
6613 COMGETTER(ChipsetType)(&aChipset);
6614
6615 if (aChipset != ChipsetType_ICH9)
6616 {
6617 return setError(E_INVALIDARG,
6618 tr("Host PCI attachment only supported with ICH9 chipset"));
6619 }
6620
6621 // check if device with this host PCI address already attached
6622 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6623 it != mHWData->mPCIDeviceAssignments.end();
6624 ++it)
6625 {
6626 LONG iHostAddress = -1;
6627 ComPtr<PCIDeviceAttachment> pAttach;
6628 pAttach = *it;
6629 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6630 if (iHostAddress == hostAddress)
6631 return setError(E_INVALIDARG,
6632 tr("Device with host PCI address already attached to this VM"));
6633 }
6634
6635 ComObjPtr<PCIDeviceAttachment> pda;
6636 char name[32];
6637
6638 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6639 Bstr bname(name);
6640 pda.createObject();
6641 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6642 setModified(IsModified_MachineData);
6643 mHWData.backup();
6644 mHWData->mPCIDeviceAssignments.push_back(pda);
6645 }
6646
6647 return S_OK;
6648}
6649
6650/**
6651 * Currently this method doesn't detach device from the running VM,
6652 * just makes sure it's not plugged on next VM start.
6653 */
6654STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6655{
6656 AutoCaller autoCaller(this);
6657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6658
6659 ComObjPtr<PCIDeviceAttachment> pAttach;
6660 bool fRemoved = false;
6661 HRESULT rc;
6662
6663 // lock scope
6664 {
6665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6666
6667 rc = checkStateDependency(MutableStateDep);
6668 if (FAILED(rc)) return rc;
6669
6670 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6671 it != mHWData->mPCIDeviceAssignments.end();
6672 ++it)
6673 {
6674 LONG iHostAddress = -1;
6675 pAttach = *it;
6676 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6677 if (iHostAddress != -1 && iHostAddress == hostAddress)
6678 {
6679 setModified(IsModified_MachineData);
6680 mHWData.backup();
6681 mHWData->mPCIDeviceAssignments.remove(pAttach);
6682 fRemoved = true;
6683 break;
6684 }
6685 }
6686 }
6687
6688
6689 /* Fire event outside of the lock */
6690 if (fRemoved)
6691 {
6692 Assert(!pAttach.isNull());
6693 ComPtr<IEventSource> es;
6694 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6695 Assert(SUCCEEDED(rc));
6696 Bstr mid;
6697 rc = this->COMGETTER(Id)(mid.asOutParam());
6698 Assert(SUCCEEDED(rc));
6699 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6700 }
6701
6702 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6703 tr("No host PCI device %08x attached"),
6704 hostAddress
6705 );
6706}
6707
6708STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6709{
6710 CheckComArgOutSafeArrayPointerValid(aAssignments);
6711
6712 AutoCaller autoCaller(this);
6713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6714
6715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6716
6717 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6718 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6719
6720 return S_OK;
6721}
6722
6723STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6724{
6725 CheckComArgOutPointerValid(aBandwidthControl);
6726
6727 AutoCaller autoCaller(this);
6728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6729
6730 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6731
6732 return S_OK;
6733}
6734
6735STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6736{
6737 CheckComArgOutPointerValid(pfEnabled);
6738 AutoCaller autoCaller(this);
6739 HRESULT hrc = autoCaller.rc();
6740 if (SUCCEEDED(hrc))
6741 {
6742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6743 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6744 }
6745 return hrc;
6746}
6747
6748STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6749{
6750 AutoCaller autoCaller(this);
6751 HRESULT hrc = autoCaller.rc();
6752 if (SUCCEEDED(hrc))
6753 {
6754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6755 hrc = checkStateDependency(MutableStateDep);
6756 if (SUCCEEDED(hrc))
6757 {
6758 hrc = mHWData.backupEx();
6759 if (SUCCEEDED(hrc))
6760 {
6761 setModified(IsModified_MachineData);
6762 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6763 }
6764 }
6765 }
6766 return hrc;
6767}
6768
6769STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6770{
6771 CheckComArgOutPointerValid(pbstrConfig);
6772 AutoCaller autoCaller(this);
6773 HRESULT hrc = autoCaller.rc();
6774 if (SUCCEEDED(hrc))
6775 {
6776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6777 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6778 }
6779 return hrc;
6780}
6781
6782STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6783{
6784 CheckComArgStr(bstrConfig);
6785 AutoCaller autoCaller(this);
6786 HRESULT hrc = autoCaller.rc();
6787 if (SUCCEEDED(hrc))
6788 {
6789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6790 hrc = checkStateDependency(MutableStateDep);
6791 if (SUCCEEDED(hrc))
6792 {
6793 hrc = mHWData.backupEx();
6794 if (SUCCEEDED(hrc))
6795 {
6796 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6797 if (SUCCEEDED(hrc))
6798 setModified(IsModified_MachineData);
6799 }
6800 }
6801 }
6802 return hrc;
6803
6804}
6805
6806STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6807{
6808 CheckComArgOutPointerValid(pfAllow);
6809 AutoCaller autoCaller(this);
6810 HRESULT hrc = autoCaller.rc();
6811 if (SUCCEEDED(hrc))
6812 {
6813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6814 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6815 }
6816 return hrc;
6817}
6818
6819STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6820{
6821 AutoCaller autoCaller(this);
6822 HRESULT hrc = autoCaller.rc();
6823 if (SUCCEEDED(hrc))
6824 {
6825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6826 hrc = checkStateDependency(MutableStateDep);
6827 if (SUCCEEDED(hrc))
6828 {
6829 hrc = mHWData.backupEx();
6830 if (SUCCEEDED(hrc))
6831 {
6832 setModified(IsModified_MachineData);
6833 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6834 }
6835 }
6836 }
6837 return hrc;
6838}
6839
6840STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6841{
6842 CheckComArgOutPointerValid(pfEnabled);
6843 AutoCaller autoCaller(this);
6844 HRESULT hrc = autoCaller.rc();
6845 if (SUCCEEDED(hrc))
6846 {
6847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6848 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6849 }
6850 return hrc;
6851}
6852
6853STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6854{
6855 AutoCaller autoCaller(this);
6856 HRESULT hrc = autoCaller.rc();
6857 if (SUCCEEDED(hrc))
6858 {
6859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6860 hrc = checkStateDependency(MutableStateDep);
6861 if ( SUCCEEDED(hrc)
6862 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6863 {
6864 AutostartDb *autostartDb = mParent->getAutostartDb();
6865 int vrc;
6866
6867 if (fEnabled)
6868 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6869 else
6870 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6871
6872 if (RT_SUCCESS(vrc))
6873 {
6874 hrc = mHWData.backupEx();
6875 if (SUCCEEDED(hrc))
6876 {
6877 setModified(IsModified_MachineData);
6878 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6879 }
6880 }
6881 else if (vrc == VERR_NOT_SUPPORTED)
6882 hrc = setError(VBOX_E_NOT_SUPPORTED,
6883 tr("The VM autostart feature is not supported on this platform"));
6884 else if (vrc == VERR_PATH_NOT_FOUND)
6885 hrc = setError(E_FAIL,
6886 tr("The path to the autostart database is not set"));
6887 else
6888 hrc = setError(E_UNEXPECTED,
6889 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6890 fEnabled ? "Adding" : "Removing",
6891 mUserData->s.strName.c_str(), vrc);
6892 }
6893 }
6894 return hrc;
6895}
6896
6897STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6898{
6899 CheckComArgOutPointerValid(puDelay);
6900 AutoCaller autoCaller(this);
6901 HRESULT hrc = autoCaller.rc();
6902 if (SUCCEEDED(hrc))
6903 {
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905 *puDelay = mHWData->mAutostart.uAutostartDelay;
6906 }
6907 return hrc;
6908}
6909
6910STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6911{
6912 AutoCaller autoCaller(this);
6913 HRESULT hrc = autoCaller.rc();
6914 if (SUCCEEDED(hrc))
6915 {
6916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6917 hrc = checkStateDependency(MutableStateDep);
6918 if (SUCCEEDED(hrc))
6919 {
6920 hrc = mHWData.backupEx();
6921 if (SUCCEEDED(hrc))
6922 {
6923 setModified(IsModified_MachineData);
6924 mHWData->mAutostart.uAutostartDelay = uDelay;
6925 }
6926 }
6927 }
6928 return hrc;
6929}
6930
6931STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6932{
6933 CheckComArgOutPointerValid(penmAutostopType);
6934 AutoCaller autoCaller(this);
6935 HRESULT hrc = autoCaller.rc();
6936 if (SUCCEEDED(hrc))
6937 {
6938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6939 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6940 }
6941 return hrc;
6942}
6943
6944STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6945{
6946 AutoCaller autoCaller(this);
6947 HRESULT hrc = autoCaller.rc();
6948 if (SUCCEEDED(hrc))
6949 {
6950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6951 hrc = checkStateDependency(MutableStateDep);
6952 if ( SUCCEEDED(hrc)
6953 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6954 {
6955 AutostartDb *autostartDb = mParent->getAutostartDb();
6956 int vrc;
6957
6958 if (enmAutostopType != AutostopType_Disabled)
6959 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6960 else
6961 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6962
6963 if (RT_SUCCESS(vrc))
6964 {
6965 hrc = mHWData.backupEx();
6966 if (SUCCEEDED(hrc))
6967 {
6968 setModified(IsModified_MachineData);
6969 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6970 }
6971 }
6972 else if (vrc == VERR_NOT_SUPPORTED)
6973 hrc = setError(VBOX_E_NOT_SUPPORTED,
6974 tr("The VM autostop feature is not supported on this platform"));
6975 else if (vrc == VERR_PATH_NOT_FOUND)
6976 hrc = setError(E_FAIL,
6977 tr("The path to the autostart database is not set"));
6978 else
6979 hrc = setError(E_UNEXPECTED,
6980 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6981 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6982 mUserData->s.strName.c_str(), vrc);
6983 }
6984 }
6985 return hrc;
6986}
6987
6988STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
6989{
6990 CheckComArgOutPointerValid(aDefaultFrontend);
6991 AutoCaller autoCaller(this);
6992 HRESULT hrc = autoCaller.rc();
6993 if (SUCCEEDED(hrc))
6994 {
6995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6996 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
6997 }
6998 return hrc;
6999}
7000
7001STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7002{
7003 CheckComArgStr(aDefaultFrontend);
7004 AutoCaller autoCaller(this);
7005 HRESULT hrc = autoCaller.rc();
7006 if (SUCCEEDED(hrc))
7007 {
7008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7009 hrc = checkStateDependency(MutableOrSavedStateDep);
7010 if (SUCCEEDED(hrc))
7011 {
7012 hrc = mHWData.backupEx();
7013 if (SUCCEEDED(hrc))
7014 {
7015 setModified(IsModified_MachineData);
7016 mHWData->mDefaultFrontend = aDefaultFrontend;
7017 }
7018 }
7019 }
7020 return hrc;
7021}
7022
7023
7024STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7025{
7026 LogFlowFuncEnter();
7027
7028 CheckComArgNotNull(pTarget);
7029 CheckComArgOutPointerValid(pProgress);
7030
7031 /* Convert the options. */
7032 RTCList<CloneOptions_T> optList;
7033 if (options != NULL)
7034 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7035
7036 if (optList.contains(CloneOptions_Link))
7037 {
7038 if (!isSnapshotMachine())
7039 return setError(E_INVALIDARG,
7040 tr("Linked clone can only be created from a snapshot"));
7041 if (mode != CloneMode_MachineState)
7042 return setError(E_INVALIDARG,
7043 tr("Linked clone can only be created for a single machine state"));
7044 }
7045 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7046
7047 AutoCaller autoCaller(this);
7048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7049
7050
7051 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7052
7053 HRESULT rc = pWorker->start(pProgress);
7054
7055 LogFlowFuncLeave();
7056
7057 return rc;
7058}
7059
7060// public methods for internal purposes
7061/////////////////////////////////////////////////////////////////////////////
7062
7063/**
7064 * Adds the given IsModified_* flag to the dirty flags of the machine.
7065 * This must be called either during loadSettings or under the machine write lock.
7066 * @param fl
7067 */
7068void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7069{
7070 mData->flModifications |= fl;
7071 if (fAllowStateModification && isStateModificationAllowed())
7072 mData->mCurrentStateModified = true;
7073}
7074
7075/**
7076 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7077 * care of the write locking.
7078 *
7079 * @param fModifications The flag to add.
7080 */
7081void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7082{
7083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7084 setModified(fModification, fAllowStateModification);
7085}
7086
7087/**
7088 * Saves the registry entry of this machine to the given configuration node.
7089 *
7090 * @param aEntryNode Node to save the registry entry to.
7091 *
7092 * @note locks this object for reading.
7093 */
7094HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7095{
7096 AutoLimitedCaller autoCaller(this);
7097 AssertComRCReturnRC(autoCaller.rc());
7098
7099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7100
7101 data.uuid = mData->mUuid;
7102 data.strSettingsFile = mData->m_strConfigFile;
7103
7104 return S_OK;
7105}
7106
7107/**
7108 * Calculates the absolute path of the given path taking the directory of the
7109 * machine settings file as the current directory.
7110 *
7111 * @param aPath Path to calculate the absolute path for.
7112 * @param aResult Where to put the result (used only on success, can be the
7113 * same Utf8Str instance as passed in @a aPath).
7114 * @return IPRT result.
7115 *
7116 * @note Locks this object for reading.
7117 */
7118int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7119{
7120 AutoCaller autoCaller(this);
7121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7122
7123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7124
7125 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7126
7127 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7128
7129 strSettingsDir.stripFilename();
7130 char folder[RTPATH_MAX];
7131 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7132 if (RT_SUCCESS(vrc))
7133 aResult = folder;
7134
7135 return vrc;
7136}
7137
7138/**
7139 * Copies strSource to strTarget, making it relative to the machine folder
7140 * if it is a subdirectory thereof, or simply copying it otherwise.
7141 *
7142 * @param strSource Path to evaluate and copy.
7143 * @param strTarget Buffer to receive target path.
7144 *
7145 * @note Locks this object for reading.
7146 */
7147void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7148 Utf8Str &strTarget)
7149{
7150 AutoCaller autoCaller(this);
7151 AssertComRCReturn(autoCaller.rc(), (void)0);
7152
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7156 // use strTarget as a temporary buffer to hold the machine settings dir
7157 strTarget = mData->m_strConfigFileFull;
7158 strTarget.stripFilename();
7159 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7160 {
7161 // is relative: then append what's left
7162 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7163 // for empty paths (only possible for subdirs) use "." to avoid
7164 // triggering default settings for not present config attributes.
7165 if (strTarget.isEmpty())
7166 strTarget = ".";
7167 }
7168 else
7169 // is not relative: then overwrite
7170 strTarget = strSource;
7171}
7172
7173/**
7174 * Returns the full path to the machine's log folder in the
7175 * \a aLogFolder argument.
7176 */
7177void Machine::getLogFolder(Utf8Str &aLogFolder)
7178{
7179 AutoCaller autoCaller(this);
7180 AssertComRCReturnVoid(autoCaller.rc());
7181
7182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7183
7184 char szTmp[RTPATH_MAX];
7185 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7186 if (RT_SUCCESS(vrc))
7187 {
7188 if (szTmp[0] && !mUserData.isNull())
7189 {
7190 char szTmp2[RTPATH_MAX];
7191 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7192 if (RT_SUCCESS(vrc))
7193 aLogFolder = BstrFmt("%s%c%s",
7194 szTmp2,
7195 RTPATH_DELIMITER,
7196 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7197 }
7198 else
7199 vrc = VERR_PATH_IS_RELATIVE;
7200 }
7201
7202 if (RT_FAILURE(vrc))
7203 {
7204 // fallback if VBOX_USER_LOGHOME is not set or invalid
7205 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7206 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7207 aLogFolder.append(RTPATH_DELIMITER);
7208 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7209 }
7210}
7211
7212/**
7213 * Returns the full path to the machine's log file for an given index.
7214 */
7215Utf8Str Machine::queryLogFilename(ULONG idx)
7216{
7217 Utf8Str logFolder;
7218 getLogFolder(logFolder);
7219 Assert(logFolder.length());
7220 Utf8Str log;
7221 if (idx == 0)
7222 log = Utf8StrFmt("%s%cVBox.log",
7223 logFolder.c_str(), RTPATH_DELIMITER);
7224 else
7225 log = Utf8StrFmt("%s%cVBox.log.%d",
7226 logFolder.c_str(), RTPATH_DELIMITER, idx);
7227 return log;
7228}
7229
7230/**
7231 * Composes a unique saved state filename based on the current system time. The filename is
7232 * granular to the second so this will work so long as no more than one snapshot is taken on
7233 * a machine per second.
7234 *
7235 * Before version 4.1, we used this formula for saved state files:
7236 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7237 * which no longer works because saved state files can now be shared between the saved state of the
7238 * "saved" machine and an online snapshot, and the following would cause problems:
7239 * 1) save machine
7240 * 2) create online snapshot from that machine state --> reusing saved state file
7241 * 3) save machine again --> filename would be reused, breaking the online snapshot
7242 *
7243 * So instead we now use a timestamp.
7244 *
7245 * @param str
7246 */
7247void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7248{
7249 AutoCaller autoCaller(this);
7250 AssertComRCReturnVoid(autoCaller.rc());
7251
7252 {
7253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7254 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7255 }
7256
7257 RTTIMESPEC ts;
7258 RTTimeNow(&ts);
7259 RTTIME time;
7260 RTTimeExplode(&time, &ts);
7261
7262 strStateFilePath += RTPATH_DELIMITER;
7263 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7264 time.i32Year, time.u8Month, time.u8MonthDay,
7265 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7266}
7267
7268/**
7269 * @note Locks this object for writing, calls the client process
7270 * (inside the lock).
7271 */
7272HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7273 const Utf8Str &strFrontend,
7274 const Utf8Str &strEnvironment,
7275 ProgressProxy *aProgress)
7276{
7277 LogFlowThisFuncEnter();
7278
7279 AssertReturn(aControl, E_FAIL);
7280 AssertReturn(aProgress, E_FAIL);
7281
7282 AutoCaller autoCaller(this);
7283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7284
7285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7286
7287 if (!mData->mRegistered)
7288 return setError(E_UNEXPECTED,
7289 tr("The machine '%s' is not registered"),
7290 mUserData->s.strName.c_str());
7291
7292 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7293
7294 if ( mData->mSession.mState == SessionState_Locked
7295 || mData->mSession.mState == SessionState_Spawning
7296 || mData->mSession.mState == SessionState_Unlocking)
7297 return setError(VBOX_E_INVALID_OBJECT_STATE,
7298 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7299 mUserData->s.strName.c_str());
7300
7301 /* may not be busy */
7302 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7303
7304 /* get the path to the executable */
7305 char szPath[RTPATH_MAX];
7306 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7307 size_t sz = strlen(szPath);
7308 szPath[sz++] = RTPATH_DELIMITER;
7309 szPath[sz] = 0;
7310 char *cmd = szPath + sz;
7311 sz = RTPATH_MAX - sz;
7312
7313 int vrc = VINF_SUCCESS;
7314 RTPROCESS pid = NIL_RTPROCESS;
7315
7316 RTENV env = RTENV_DEFAULT;
7317
7318 if (!strEnvironment.isEmpty())
7319 {
7320 char *newEnvStr = NULL;
7321
7322 do
7323 {
7324 /* clone the current environment */
7325 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7326 AssertRCBreakStmt(vrc2, vrc = vrc2);
7327
7328 newEnvStr = RTStrDup(strEnvironment.c_str());
7329 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7330
7331 /* put new variables to the environment
7332 * (ignore empty variable names here since RTEnv API
7333 * intentionally doesn't do that) */
7334 char *var = newEnvStr;
7335 for (char *p = newEnvStr; *p; ++p)
7336 {
7337 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7338 {
7339 *p = '\0';
7340 if (*var)
7341 {
7342 char *val = strchr(var, '=');
7343 if (val)
7344 {
7345 *val++ = '\0';
7346 vrc2 = RTEnvSetEx(env, var, val);
7347 }
7348 else
7349 vrc2 = RTEnvUnsetEx(env, var);
7350 if (RT_FAILURE(vrc2))
7351 break;
7352 }
7353 var = p + 1;
7354 }
7355 }
7356 if (RT_SUCCESS(vrc2) && *var)
7357 vrc2 = RTEnvPutEx(env, var);
7358
7359 AssertRCBreakStmt(vrc2, vrc = vrc2);
7360 }
7361 while (0);
7362
7363 if (newEnvStr != NULL)
7364 RTStrFree(newEnvStr);
7365 }
7366
7367 /* Qt is default */
7368#ifdef VBOX_WITH_QTGUI
7369 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7370 {
7371# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7372 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7373# else
7374 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7375# endif
7376 Assert(sz >= sizeof(VirtualBox_exe));
7377 strcpy(cmd, VirtualBox_exe);
7378
7379 Utf8Str idStr = mData->mUuid.toString();
7380 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7381 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7382 }
7383#else /* !VBOX_WITH_QTGUI */
7384 if (0)
7385 ;
7386#endif /* VBOX_WITH_QTGUI */
7387
7388 else
7389
7390#ifdef VBOX_WITH_VBOXSDL
7391 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7392 {
7393 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7394 Assert(sz >= sizeof(VBoxSDL_exe));
7395 strcpy(cmd, VBoxSDL_exe);
7396
7397 Utf8Str idStr = mData->mUuid.toString();
7398 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7399 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7400 }
7401#else /* !VBOX_WITH_VBOXSDL */
7402 if (0)
7403 ;
7404#endif /* !VBOX_WITH_VBOXSDL */
7405
7406 else
7407
7408#ifdef VBOX_WITH_HEADLESS
7409 if ( strFrontend == "headless"
7410 || strFrontend == "capture"
7411 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7412 )
7413 {
7414 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7415 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7416 * and a VM works even if the server has not been installed.
7417 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7418 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7419 * differently in 4.0 and 3.x.
7420 */
7421 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7422 Assert(sz >= sizeof(VBoxHeadless_exe));
7423 strcpy(cmd, VBoxHeadless_exe);
7424
7425 Utf8Str idStr = mData->mUuid.toString();
7426 /* Leave space for "--capture" arg. */
7427 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7428 "--startvm", idStr.c_str(),
7429 "--vrde", "config",
7430 0, /* For "--capture". */
7431 0 };
7432 if (strFrontend == "capture")
7433 {
7434 unsigned pos = RT_ELEMENTS(args) - 2;
7435 args[pos] = "--capture";
7436 }
7437 vrc = RTProcCreate(szPath, args, env,
7438#ifdef RT_OS_WINDOWS
7439 RTPROC_FLAGS_NO_WINDOW
7440#else
7441 0
7442#endif
7443 , &pid);
7444 }
7445#else /* !VBOX_WITH_HEADLESS */
7446 if (0)
7447 ;
7448#endif /* !VBOX_WITH_HEADLESS */
7449 else
7450 {
7451 RTEnvDestroy(env);
7452 return setError(E_INVALIDARG,
7453 tr("Invalid frontend name: '%s'"),
7454 strFrontend.c_str());
7455 }
7456
7457 RTEnvDestroy(env);
7458
7459 if (RT_FAILURE(vrc))
7460 return setError(VBOX_E_IPRT_ERROR,
7461 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7462 mUserData->s.strName.c_str(), vrc);
7463
7464 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7465
7466 /*
7467 * Note that we don't release the lock here before calling the client,
7468 * because it doesn't need to call us back if called with a NULL argument.
7469 * Releasing the lock here is dangerous because we didn't prepare the
7470 * launch data yet, but the client we've just started may happen to be
7471 * too fast and call openSession() that will fail (because of PID, etc.),
7472 * so that the Machine will never get out of the Spawning session state.
7473 */
7474
7475 /* inform the session that it will be a remote one */
7476 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7477 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7478 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7479
7480 if (FAILED(rc))
7481 {
7482 /* restore the session state */
7483 mData->mSession.mState = SessionState_Unlocked;
7484 /* The failure may occur w/o any error info (from RPC), so provide one */
7485 return setError(VBOX_E_VM_ERROR,
7486 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7487 }
7488
7489 /* attach launch data to the machine */
7490 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7491 mData->mSession.mRemoteControls.push_back(aControl);
7492 mData->mSession.mProgress = aProgress;
7493 mData->mSession.mPID = pid;
7494 mData->mSession.mState = SessionState_Spawning;
7495 mData->mSession.mType = strFrontend;
7496
7497 LogFlowThisFuncLeave();
7498 return S_OK;
7499}
7500
7501/**
7502 * Returns @c true if the given machine has an open direct session and returns
7503 * the session machine instance and additional session data (on some platforms)
7504 * if so.
7505 *
7506 * Note that when the method returns @c false, the arguments remain unchanged.
7507 *
7508 * @param aMachine Session machine object.
7509 * @param aControl Direct session control object (optional).
7510 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7511 *
7512 * @note locks this object for reading.
7513 */
7514#if defined(RT_OS_WINDOWS)
7515bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7516 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7517 HANDLE *aIPCSem /*= NULL*/,
7518 bool aAllowClosing /*= false*/)
7519#elif defined(RT_OS_OS2)
7520bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7521 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7522 HMTX *aIPCSem /*= NULL*/,
7523 bool aAllowClosing /*= false*/)
7524#else
7525bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7526 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7527 bool aAllowClosing /*= false*/)
7528#endif
7529{
7530 AutoLimitedCaller autoCaller(this);
7531 AssertComRCReturn(autoCaller.rc(), false);
7532
7533 /* just return false for inaccessible machines */
7534 if (autoCaller.state() != Ready)
7535 return false;
7536
7537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7538
7539 if ( mData->mSession.mState == SessionState_Locked
7540 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7541 )
7542 {
7543 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7544
7545 aMachine = mData->mSession.mMachine;
7546
7547 if (aControl != NULL)
7548 *aControl = mData->mSession.mDirectControl;
7549
7550#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7551 /* Additional session data */
7552 if (aIPCSem != NULL)
7553 *aIPCSem = aMachine->mIPCSem;
7554#endif
7555 return true;
7556 }
7557
7558 return false;
7559}
7560
7561/**
7562 * Returns @c true if the given machine has an spawning direct session and
7563 * returns and additional session data (on some platforms) if so.
7564 *
7565 * Note that when the method returns @c false, the arguments remain unchanged.
7566 *
7567 * @param aPID PID of the spawned direct session process.
7568 *
7569 * @note locks this object for reading.
7570 */
7571#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7572bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7573#else
7574bool Machine::isSessionSpawning()
7575#endif
7576{
7577 AutoLimitedCaller autoCaller(this);
7578 AssertComRCReturn(autoCaller.rc(), false);
7579
7580 /* just return false for inaccessible machines */
7581 if (autoCaller.state() != Ready)
7582 return false;
7583
7584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7585
7586 if (mData->mSession.mState == SessionState_Spawning)
7587 {
7588#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7589 /* Additional session data */
7590 if (aPID != NULL)
7591 {
7592 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7593 *aPID = mData->mSession.mPID;
7594 }
7595#endif
7596 return true;
7597 }
7598
7599 return false;
7600}
7601
7602/**
7603 * Called from the client watcher thread to check for unexpected client process
7604 * death during Session_Spawning state (e.g. before it successfully opened a
7605 * direct session).
7606 *
7607 * On Win32 and on OS/2, this method is called only when we've got the
7608 * direct client's process termination notification, so it always returns @c
7609 * true.
7610 *
7611 * On other platforms, this method returns @c true if the client process is
7612 * terminated and @c false if it's still alive.
7613 *
7614 * @note Locks this object for writing.
7615 */
7616bool Machine::checkForSpawnFailure()
7617{
7618 AutoCaller autoCaller(this);
7619 if (!autoCaller.isOk())
7620 {
7621 /* nothing to do */
7622 LogFlowThisFunc(("Already uninitialized!\n"));
7623 return true;
7624 }
7625
7626 /* VirtualBox::addProcessToReap() needs a write lock */
7627 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7628
7629 if (mData->mSession.mState != SessionState_Spawning)
7630 {
7631 /* nothing to do */
7632 LogFlowThisFunc(("Not spawning any more!\n"));
7633 return true;
7634 }
7635
7636 HRESULT rc = S_OK;
7637
7638#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7639
7640 /* the process was already unexpectedly terminated, we just need to set an
7641 * error and finalize session spawning */
7642 rc = setError(E_FAIL,
7643 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7644 getName().c_str());
7645#else
7646
7647 /* PID not yet initialized, skip check. */
7648 if (mData->mSession.mPID == NIL_RTPROCESS)
7649 return false;
7650
7651 RTPROCSTATUS status;
7652 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7653 &status);
7654
7655 if (vrc != VERR_PROCESS_RUNNING)
7656 {
7657 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7658 rc = setError(E_FAIL,
7659 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7660 getName().c_str(), status.iStatus);
7661 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7662 rc = setError(E_FAIL,
7663 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7664 getName().c_str(), status.iStatus);
7665 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7666 rc = setError(E_FAIL,
7667 tr("The virtual machine '%s' has terminated abnormally"),
7668 getName().c_str(), status.iStatus);
7669 else
7670 rc = setError(E_FAIL,
7671 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7672 getName().c_str(), rc);
7673 }
7674
7675#endif
7676
7677 if (FAILED(rc))
7678 {
7679 /* Close the remote session, remove the remote control from the list
7680 * and reset session state to Closed (@note keep the code in sync with
7681 * the relevant part in checkForSpawnFailure()). */
7682
7683 Assert(mData->mSession.mRemoteControls.size() == 1);
7684 if (mData->mSession.mRemoteControls.size() == 1)
7685 {
7686 ErrorInfoKeeper eik;
7687 mData->mSession.mRemoteControls.front()->Uninitialize();
7688 }
7689
7690 mData->mSession.mRemoteControls.clear();
7691 mData->mSession.mState = SessionState_Unlocked;
7692
7693 /* finalize the progress after setting the state */
7694 if (!mData->mSession.mProgress.isNull())
7695 {
7696 mData->mSession.mProgress->notifyComplete(rc);
7697 mData->mSession.mProgress.setNull();
7698 }
7699
7700 mParent->addProcessToReap(mData->mSession.mPID);
7701 mData->mSession.mPID = NIL_RTPROCESS;
7702
7703 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7704 return true;
7705 }
7706
7707 return false;
7708}
7709
7710/**
7711 * Checks whether the machine can be registered. If so, commits and saves
7712 * all settings.
7713 *
7714 * @note Must be called from mParent's write lock. Locks this object and
7715 * children for writing.
7716 */
7717HRESULT Machine::prepareRegister()
7718{
7719 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7720
7721 AutoLimitedCaller autoCaller(this);
7722 AssertComRCReturnRC(autoCaller.rc());
7723
7724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7725
7726 /* wait for state dependents to drop to zero */
7727 ensureNoStateDependencies();
7728
7729 if (!mData->mAccessible)
7730 return setError(VBOX_E_INVALID_OBJECT_STATE,
7731 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7732 mUserData->s.strName.c_str(),
7733 mData->mUuid.toString().c_str());
7734
7735 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7736
7737 if (mData->mRegistered)
7738 return setError(VBOX_E_INVALID_OBJECT_STATE,
7739 tr("The machine '%s' with UUID {%s} is already registered"),
7740 mUserData->s.strName.c_str(),
7741 mData->mUuid.toString().c_str());
7742
7743 HRESULT rc = S_OK;
7744
7745 // Ensure the settings are saved. If we are going to be registered and
7746 // no config file exists yet, create it by calling saveSettings() too.
7747 if ( (mData->flModifications)
7748 || (!mData->pMachineConfigFile->fileExists())
7749 )
7750 {
7751 rc = saveSettings(NULL);
7752 // no need to check whether VirtualBox.xml needs saving too since
7753 // we can't have a machine XML file rename pending
7754 if (FAILED(rc)) return rc;
7755 }
7756
7757 /* more config checking goes here */
7758
7759 if (SUCCEEDED(rc))
7760 {
7761 /* we may have had implicit modifications we want to fix on success */
7762 commit();
7763
7764 mData->mRegistered = true;
7765 }
7766 else
7767 {
7768 /* we may have had implicit modifications we want to cancel on failure*/
7769 rollback(false /* aNotify */);
7770 }
7771
7772 return rc;
7773}
7774
7775/**
7776 * Increases the number of objects dependent on the machine state or on the
7777 * registered state. Guarantees that these two states will not change at least
7778 * until #releaseStateDependency() is called.
7779 *
7780 * Depending on the @a aDepType value, additional state checks may be made.
7781 * These checks will set extended error info on failure. See
7782 * #checkStateDependency() for more info.
7783 *
7784 * If this method returns a failure, the dependency is not added and the caller
7785 * is not allowed to rely on any particular machine state or registration state
7786 * value and may return the failed result code to the upper level.
7787 *
7788 * @param aDepType Dependency type to add.
7789 * @param aState Current machine state (NULL if not interested).
7790 * @param aRegistered Current registered state (NULL if not interested).
7791 *
7792 * @note Locks this object for writing.
7793 */
7794HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7795 MachineState_T *aState /* = NULL */,
7796 BOOL *aRegistered /* = NULL */)
7797{
7798 AutoCaller autoCaller(this);
7799 AssertComRCReturnRC(autoCaller.rc());
7800
7801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7802
7803 HRESULT rc = checkStateDependency(aDepType);
7804 if (FAILED(rc)) return rc;
7805
7806 {
7807 if (mData->mMachineStateChangePending != 0)
7808 {
7809 /* ensureNoStateDependencies() is waiting for state dependencies to
7810 * drop to zero so don't add more. It may make sense to wait a bit
7811 * and retry before reporting an error (since the pending state
7812 * transition should be really quick) but let's just assert for
7813 * now to see if it ever happens on practice. */
7814
7815 AssertFailed();
7816
7817 return setError(E_ACCESSDENIED,
7818 tr("Machine state change is in progress. Please retry the operation later."));
7819 }
7820
7821 ++mData->mMachineStateDeps;
7822 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7823 }
7824
7825 if (aState)
7826 *aState = mData->mMachineState;
7827 if (aRegistered)
7828 *aRegistered = mData->mRegistered;
7829
7830 return S_OK;
7831}
7832
7833/**
7834 * Decreases the number of objects dependent on the machine state.
7835 * Must always complete the #addStateDependency() call after the state
7836 * dependency is no more necessary.
7837 */
7838void Machine::releaseStateDependency()
7839{
7840 AutoCaller autoCaller(this);
7841 AssertComRCReturnVoid(autoCaller.rc());
7842
7843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7844
7845 /* releaseStateDependency() w/o addStateDependency()? */
7846 AssertReturnVoid(mData->mMachineStateDeps != 0);
7847 -- mData->mMachineStateDeps;
7848
7849 if (mData->mMachineStateDeps == 0)
7850 {
7851 /* inform ensureNoStateDependencies() that there are no more deps */
7852 if (mData->mMachineStateChangePending != 0)
7853 {
7854 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7855 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7856 }
7857 }
7858}
7859
7860// protected methods
7861/////////////////////////////////////////////////////////////////////////////
7862
7863/**
7864 * Performs machine state checks based on the @a aDepType value. If a check
7865 * fails, this method will set extended error info, otherwise it will return
7866 * S_OK. It is supposed, that on failure, the caller will immediately return
7867 * the return value of this method to the upper level.
7868 *
7869 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7870 *
7871 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7872 * current state of this machine object allows to change settings of the
7873 * machine (i.e. the machine is not registered, or registered but not running
7874 * and not saved). It is useful to call this method from Machine setters
7875 * before performing any change.
7876 *
7877 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7878 * as for MutableStateDep except that if the machine is saved, S_OK is also
7879 * returned. This is useful in setters which allow changing machine
7880 * properties when it is in the saved state.
7881 *
7882 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7883 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7884 * Aborted).
7885 *
7886 * @param aDepType Dependency type to check.
7887 *
7888 * @note Non Machine based classes should use #addStateDependency() and
7889 * #releaseStateDependency() methods or the smart AutoStateDependency
7890 * template.
7891 *
7892 * @note This method must be called from under this object's read or write
7893 * lock.
7894 */
7895HRESULT Machine::checkStateDependency(StateDependency aDepType)
7896{
7897 switch (aDepType)
7898 {
7899 case AnyStateDep:
7900 {
7901 break;
7902 }
7903 case MutableStateDep:
7904 {
7905 if ( mData->mRegistered
7906 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7907 || ( mData->mMachineState != MachineState_Paused
7908 && mData->mMachineState != MachineState_Running
7909 && mData->mMachineState != MachineState_Aborted
7910 && mData->mMachineState != MachineState_Teleported
7911 && mData->mMachineState != MachineState_PoweredOff
7912 )
7913 )
7914 )
7915 return setError(VBOX_E_INVALID_VM_STATE,
7916 tr("The machine is not mutable (state is %s)"),
7917 Global::stringifyMachineState(mData->mMachineState));
7918 break;
7919 }
7920 case MutableOrSavedStateDep:
7921 {
7922 if ( mData->mRegistered
7923 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7924 || ( mData->mMachineState != MachineState_Paused
7925 && mData->mMachineState != MachineState_Running
7926 && mData->mMachineState != MachineState_Aborted
7927 && mData->mMachineState != MachineState_Teleported
7928 && mData->mMachineState != MachineState_Saved
7929 && mData->mMachineState != MachineState_PoweredOff
7930 )
7931 )
7932 )
7933 return setError(VBOX_E_INVALID_VM_STATE,
7934 tr("The machine is not mutable (state is %s)"),
7935 Global::stringifyMachineState(mData->mMachineState));
7936 break;
7937 }
7938 case OfflineStateDep:
7939 {
7940 if ( mData->mRegistered
7941 && ( !isSessionMachine()
7942 || ( mData->mMachineState != MachineState_PoweredOff
7943 && mData->mMachineState != MachineState_Saved
7944 && mData->mMachineState != MachineState_Aborted
7945 && mData->mMachineState != MachineState_Teleported
7946 )
7947 )
7948 )
7949 return setError(VBOX_E_INVALID_VM_STATE,
7950 tr("The machine is not offline (state is %s)"),
7951 Global::stringifyMachineState(mData->mMachineState));
7952 break;
7953 }
7954 }
7955
7956 return S_OK;
7957}
7958
7959/**
7960 * Helper to initialize all associated child objects and allocate data
7961 * structures.
7962 *
7963 * This method must be called as a part of the object's initialization procedure
7964 * (usually done in the #init() method).
7965 *
7966 * @note Must be called only from #init() or from #registeredInit().
7967 */
7968HRESULT Machine::initDataAndChildObjects()
7969{
7970 AutoCaller autoCaller(this);
7971 AssertComRCReturnRC(autoCaller.rc());
7972 AssertComRCReturn(autoCaller.state() == InInit ||
7973 autoCaller.state() == Limited, E_FAIL);
7974
7975 AssertReturn(!mData->mAccessible, E_FAIL);
7976
7977 /* allocate data structures */
7978 mSSData.allocate();
7979 mUserData.allocate();
7980 mHWData.allocate();
7981 mMediaData.allocate();
7982 mStorageControllers.allocate();
7983
7984 /* initialize mOSTypeId */
7985 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7986
7987 /* create associated BIOS settings object */
7988 unconst(mBIOSSettings).createObject();
7989 mBIOSSettings->init(this);
7990
7991 /* create an associated VRDE object (default is disabled) */
7992 unconst(mVRDEServer).createObject();
7993 mVRDEServer->init(this);
7994
7995 /* create associated serial port objects */
7996 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7997 {
7998 unconst(mSerialPorts[slot]).createObject();
7999 mSerialPorts[slot]->init(this, slot);
8000 }
8001
8002 /* create associated parallel port objects */
8003 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8004 {
8005 unconst(mParallelPorts[slot]).createObject();
8006 mParallelPorts[slot]->init(this, slot);
8007 }
8008
8009 /* create the audio adapter object (always present, default is disabled) */
8010 unconst(mAudioAdapter).createObject();
8011 mAudioAdapter->init(this);
8012
8013 /* create the USB controller object (always present, default is disabled) */
8014 unconst(mUSBController).createObject();
8015 mUSBController->init(this);
8016
8017 /* create associated network adapter objects */
8018 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8019 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8020 {
8021 unconst(mNetworkAdapters[slot]).createObject();
8022 mNetworkAdapters[slot]->init(this, slot);
8023 }
8024
8025 /* create the bandwidth control */
8026 unconst(mBandwidthControl).createObject();
8027 mBandwidthControl->init(this);
8028
8029 return S_OK;
8030}
8031
8032/**
8033 * Helper to uninitialize all associated child objects and to free all data
8034 * structures.
8035 *
8036 * This method must be called as a part of the object's uninitialization
8037 * procedure (usually done in the #uninit() method).
8038 *
8039 * @note Must be called only from #uninit() or from #registeredInit().
8040 */
8041void Machine::uninitDataAndChildObjects()
8042{
8043 AutoCaller autoCaller(this);
8044 AssertComRCReturnVoid(autoCaller.rc());
8045 AssertComRCReturnVoid( autoCaller.state() == InUninit
8046 || autoCaller.state() == Limited);
8047
8048 /* tell all our other child objects we've been uninitialized */
8049 if (mBandwidthControl)
8050 {
8051 mBandwidthControl->uninit();
8052 unconst(mBandwidthControl).setNull();
8053 }
8054
8055 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8056 {
8057 if (mNetworkAdapters[slot])
8058 {
8059 mNetworkAdapters[slot]->uninit();
8060 unconst(mNetworkAdapters[slot]).setNull();
8061 }
8062 }
8063
8064 if (mUSBController)
8065 {
8066 mUSBController->uninit();
8067 unconst(mUSBController).setNull();
8068 }
8069
8070 if (mAudioAdapter)
8071 {
8072 mAudioAdapter->uninit();
8073 unconst(mAudioAdapter).setNull();
8074 }
8075
8076 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8077 {
8078 if (mParallelPorts[slot])
8079 {
8080 mParallelPorts[slot]->uninit();
8081 unconst(mParallelPorts[slot]).setNull();
8082 }
8083 }
8084
8085 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8086 {
8087 if (mSerialPorts[slot])
8088 {
8089 mSerialPorts[slot]->uninit();
8090 unconst(mSerialPorts[slot]).setNull();
8091 }
8092 }
8093
8094 if (mVRDEServer)
8095 {
8096 mVRDEServer->uninit();
8097 unconst(mVRDEServer).setNull();
8098 }
8099
8100 if (mBIOSSettings)
8101 {
8102 mBIOSSettings->uninit();
8103 unconst(mBIOSSettings).setNull();
8104 }
8105
8106 /* Deassociate media (only when a real Machine or a SnapshotMachine
8107 * instance is uninitialized; SessionMachine instances refer to real
8108 * Machine media). This is necessary for a clean re-initialization of
8109 * the VM after successfully re-checking the accessibility state. Note
8110 * that in case of normal Machine or SnapshotMachine uninitialization (as
8111 * a result of unregistering or deleting the snapshot), outdated media
8112 * attachments will already be uninitialized and deleted, so this
8113 * code will not affect them. */
8114 if ( !!mMediaData
8115 && (!isSessionMachine())
8116 )
8117 {
8118 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8119 it != mMediaData->mAttachments.end();
8120 ++it)
8121 {
8122 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8123 if (pMedium.isNull())
8124 continue;
8125 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8126 AssertComRC(rc);
8127 }
8128 }
8129
8130 if (!isSessionMachine() && !isSnapshotMachine())
8131 {
8132 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8133 if (mData->mFirstSnapshot)
8134 {
8135 // snapshots tree is protected by machine write lock; strictly
8136 // this isn't necessary here since we're deleting the entire
8137 // machine, but otherwise we assert in Snapshot::uninit()
8138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8139 mData->mFirstSnapshot->uninit();
8140 mData->mFirstSnapshot.setNull();
8141 }
8142
8143 mData->mCurrentSnapshot.setNull();
8144 }
8145
8146 /* free data structures (the essential mData structure is not freed here
8147 * since it may be still in use) */
8148 mMediaData.free();
8149 mStorageControllers.free();
8150 mHWData.free();
8151 mUserData.free();
8152 mSSData.free();
8153}
8154
8155/**
8156 * Returns a pointer to the Machine object for this machine that acts like a
8157 * parent for complex machine data objects such as shared folders, etc.
8158 *
8159 * For primary Machine objects and for SnapshotMachine objects, returns this
8160 * object's pointer itself. For SessionMachine objects, returns the peer
8161 * (primary) machine pointer.
8162 */
8163Machine* Machine::getMachine()
8164{
8165 if (isSessionMachine())
8166 return (Machine*)mPeer;
8167 return this;
8168}
8169
8170/**
8171 * Makes sure that there are no machine state dependents. If necessary, waits
8172 * for the number of dependents to drop to zero.
8173 *
8174 * Make sure this method is called from under this object's write lock to
8175 * guarantee that no new dependents may be added when this method returns
8176 * control to the caller.
8177 *
8178 * @note Locks this object for writing. The lock will be released while waiting
8179 * (if necessary).
8180 *
8181 * @warning To be used only in methods that change the machine state!
8182 */
8183void Machine::ensureNoStateDependencies()
8184{
8185 AssertReturnVoid(isWriteLockOnCurrentThread());
8186
8187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8188
8189 /* Wait for all state dependents if necessary */
8190 if (mData->mMachineStateDeps != 0)
8191 {
8192 /* lazy semaphore creation */
8193 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8194 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8195
8196 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8197 mData->mMachineStateDeps));
8198
8199 ++mData->mMachineStateChangePending;
8200
8201 /* reset the semaphore before waiting, the last dependent will signal
8202 * it */
8203 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8204
8205 alock.release();
8206
8207 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8208
8209 alock.acquire();
8210
8211 -- mData->mMachineStateChangePending;
8212 }
8213}
8214
8215/**
8216 * Changes the machine state and informs callbacks.
8217 *
8218 * This method is not intended to fail so it either returns S_OK or asserts (and
8219 * returns a failure).
8220 *
8221 * @note Locks this object for writing.
8222 */
8223HRESULT Machine::setMachineState(MachineState_T aMachineState)
8224{
8225 LogFlowThisFuncEnter();
8226 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8227
8228 AutoCaller autoCaller(this);
8229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8230
8231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8232
8233 /* wait for state dependents to drop to zero */
8234 ensureNoStateDependencies();
8235
8236 if (mData->mMachineState != aMachineState)
8237 {
8238 mData->mMachineState = aMachineState;
8239
8240 RTTimeNow(&mData->mLastStateChange);
8241
8242 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8243 }
8244
8245 LogFlowThisFuncLeave();
8246 return S_OK;
8247}
8248
8249/**
8250 * Searches for a shared folder with the given logical name
8251 * in the collection of shared folders.
8252 *
8253 * @param aName logical name of the shared folder
8254 * @param aSharedFolder where to return the found object
8255 * @param aSetError whether to set the error info if the folder is
8256 * not found
8257 * @return
8258 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8259 *
8260 * @note
8261 * must be called from under the object's lock!
8262 */
8263HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8264 ComObjPtr<SharedFolder> &aSharedFolder,
8265 bool aSetError /* = false */)
8266{
8267 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8268 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8269 it != mHWData->mSharedFolders.end();
8270 ++it)
8271 {
8272 SharedFolder *pSF = *it;
8273 AutoCaller autoCaller(pSF);
8274 if (pSF->getName() == aName)
8275 {
8276 aSharedFolder = pSF;
8277 rc = S_OK;
8278 break;
8279 }
8280 }
8281
8282 if (aSetError && FAILED(rc))
8283 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8284
8285 return rc;
8286}
8287
8288/**
8289 * Initializes all machine instance data from the given settings structures
8290 * from XML. The exception is the machine UUID which needs special handling
8291 * depending on the caller's use case, so the caller needs to set that herself.
8292 *
8293 * This gets called in several contexts during machine initialization:
8294 *
8295 * -- When machine XML exists on disk already and needs to be loaded into memory,
8296 * for example, from registeredInit() to load all registered machines on
8297 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8298 * attached to the machine should be part of some media registry already.
8299 *
8300 * -- During OVF import, when a machine config has been constructed from an
8301 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8302 * ensure that the media listed as attachments in the config (which have
8303 * been imported from the OVF) receive the correct registry ID.
8304 *
8305 * -- During VM cloning.
8306 *
8307 * @param config Machine settings from XML.
8308 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8309 * @return
8310 */
8311HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8312 const Guid *puuidRegistry)
8313{
8314 // copy name, description, OS type, teleporter, UTC etc.
8315 mUserData->s = config.machineUserData;
8316
8317 // look up the object by Id to check it is valid
8318 ComPtr<IGuestOSType> guestOSType;
8319 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8320 guestOSType.asOutParam());
8321 if (FAILED(rc)) return rc;
8322
8323 // stateFile (optional)
8324 if (config.strStateFile.isEmpty())
8325 mSSData->strStateFilePath.setNull();
8326 else
8327 {
8328 Utf8Str stateFilePathFull(config.strStateFile);
8329 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8330 if (RT_FAILURE(vrc))
8331 return setError(E_FAIL,
8332 tr("Invalid saved state file path '%s' (%Rrc)"),
8333 config.strStateFile.c_str(),
8334 vrc);
8335 mSSData->strStateFilePath = stateFilePathFull;
8336 }
8337
8338 // snapshot folder needs special processing so set it again
8339 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8340 if (FAILED(rc)) return rc;
8341
8342 /* Copy the extra data items (Not in any case config is already the same as
8343 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8344 * make sure the extra data map is copied). */
8345 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8346
8347 /* currentStateModified (optional, default is true) */
8348 mData->mCurrentStateModified = config.fCurrentStateModified;
8349
8350 mData->mLastStateChange = config.timeLastStateChange;
8351
8352 /*
8353 * note: all mUserData members must be assigned prior this point because
8354 * we need to commit changes in order to let mUserData be shared by all
8355 * snapshot machine instances.
8356 */
8357 mUserData.commitCopy();
8358
8359 // machine registry, if present (must be loaded before snapshots)
8360 if (config.canHaveOwnMediaRegistry())
8361 {
8362 // determine machine folder
8363 Utf8Str strMachineFolder = getSettingsFileFull();
8364 strMachineFolder.stripFilename();
8365 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8366 config.mediaRegistry,
8367 strMachineFolder);
8368 if (FAILED(rc)) return rc;
8369 }
8370
8371 /* Snapshot node (optional) */
8372 size_t cRootSnapshots;
8373 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8374 {
8375 // there must be only one root snapshot
8376 Assert(cRootSnapshots == 1);
8377
8378 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8379
8380 rc = loadSnapshot(snap,
8381 config.uuidCurrentSnapshot,
8382 NULL); // no parent == first snapshot
8383 if (FAILED(rc)) return rc;
8384 }
8385
8386 // hardware data
8387 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8388 if (FAILED(rc)) return rc;
8389
8390 // load storage controllers
8391 rc = loadStorageControllers(config.storageMachine,
8392 puuidRegistry,
8393 NULL /* puuidSnapshot */);
8394 if (FAILED(rc)) return rc;
8395
8396 /*
8397 * NOTE: the assignment below must be the last thing to do,
8398 * otherwise it will be not possible to change the settings
8399 * somewhere in the code above because all setters will be
8400 * blocked by checkStateDependency(MutableStateDep).
8401 */
8402
8403 /* set the machine state to Aborted or Saved when appropriate */
8404 if (config.fAborted)
8405 {
8406 mSSData->strStateFilePath.setNull();
8407
8408 /* no need to use setMachineState() during init() */
8409 mData->mMachineState = MachineState_Aborted;
8410 }
8411 else if (!mSSData->strStateFilePath.isEmpty())
8412 {
8413 /* no need to use setMachineState() during init() */
8414 mData->mMachineState = MachineState_Saved;
8415 }
8416
8417 // after loading settings, we are no longer different from the XML on disk
8418 mData->flModifications = 0;
8419
8420 return S_OK;
8421}
8422
8423/**
8424 * Recursively loads all snapshots starting from the given.
8425 *
8426 * @param aNode <Snapshot> node.
8427 * @param aCurSnapshotId Current snapshot ID from the settings file.
8428 * @param aParentSnapshot Parent snapshot.
8429 */
8430HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8431 const Guid &aCurSnapshotId,
8432 Snapshot *aParentSnapshot)
8433{
8434 AssertReturn(!isSnapshotMachine(), E_FAIL);
8435 AssertReturn(!isSessionMachine(), E_FAIL);
8436
8437 HRESULT rc = S_OK;
8438
8439 Utf8Str strStateFile;
8440 if (!data.strStateFile.isEmpty())
8441 {
8442 /* optional */
8443 strStateFile = data.strStateFile;
8444 int vrc = calculateFullPath(strStateFile, strStateFile);
8445 if (RT_FAILURE(vrc))
8446 return setError(E_FAIL,
8447 tr("Invalid saved state file path '%s' (%Rrc)"),
8448 strStateFile.c_str(),
8449 vrc);
8450 }
8451
8452 /* create a snapshot machine object */
8453 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8454 pSnapshotMachine.createObject();
8455 rc = pSnapshotMachine->initFromSettings(this,
8456 data.hardware,
8457 &data.debugging,
8458 &data.autostart,
8459 data.storage,
8460 data.uuid.ref(),
8461 strStateFile);
8462 if (FAILED(rc)) return rc;
8463
8464 /* create a snapshot object */
8465 ComObjPtr<Snapshot> pSnapshot;
8466 pSnapshot.createObject();
8467 /* initialize the snapshot */
8468 rc = pSnapshot->init(mParent, // VirtualBox object
8469 data.uuid,
8470 data.strName,
8471 data.strDescription,
8472 data.timestamp,
8473 pSnapshotMachine,
8474 aParentSnapshot);
8475 if (FAILED(rc)) return rc;
8476
8477 /* memorize the first snapshot if necessary */
8478 if (!mData->mFirstSnapshot)
8479 mData->mFirstSnapshot = pSnapshot;
8480
8481 /* memorize the current snapshot when appropriate */
8482 if ( !mData->mCurrentSnapshot
8483 && pSnapshot->getId() == aCurSnapshotId
8484 )
8485 mData->mCurrentSnapshot = pSnapshot;
8486
8487 // now create the children
8488 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8489 it != data.llChildSnapshots.end();
8490 ++it)
8491 {
8492 const settings::Snapshot &childData = *it;
8493 // recurse
8494 rc = loadSnapshot(childData,
8495 aCurSnapshotId,
8496 pSnapshot); // parent = the one we created above
8497 if (FAILED(rc)) return rc;
8498 }
8499
8500 return rc;
8501}
8502
8503/**
8504 * Loads settings into mHWData.
8505 *
8506 * @param data Reference to the hardware settings.
8507 * @param pDbg Pointer to the debugging settings.
8508 * @param pAutostart Pointer to the autostart settings.
8509 */
8510HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8511 const settings::Autostart *pAutostart)
8512{
8513 AssertReturn(!isSessionMachine(), E_FAIL);
8514
8515 HRESULT rc = S_OK;
8516
8517 try
8518 {
8519 /* The hardware version attribute (optional). */
8520 mHWData->mHWVersion = data.strVersion;
8521 mHWData->mHardwareUUID = data.uuid;
8522
8523 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8524 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8525 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8526 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8527 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8528 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8529 mHWData->mPAEEnabled = data.fPAE;
8530 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8531
8532 mHWData->mCPUCount = data.cCPUs;
8533 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8534 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8535
8536 // cpu
8537 if (mHWData->mCPUHotPlugEnabled)
8538 {
8539 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8540 it != data.llCpus.end();
8541 ++it)
8542 {
8543 const settings::Cpu &cpu = *it;
8544
8545 mHWData->mCPUAttached[cpu.ulId] = true;
8546 }
8547 }
8548
8549 // cpuid leafs
8550 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8551 it != data.llCpuIdLeafs.end();
8552 ++it)
8553 {
8554 const settings::CpuIdLeaf &leaf = *it;
8555
8556 switch (leaf.ulId)
8557 {
8558 case 0x0:
8559 case 0x1:
8560 case 0x2:
8561 case 0x3:
8562 case 0x4:
8563 case 0x5:
8564 case 0x6:
8565 case 0x7:
8566 case 0x8:
8567 case 0x9:
8568 case 0xA:
8569 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8570 break;
8571
8572 case 0x80000000:
8573 case 0x80000001:
8574 case 0x80000002:
8575 case 0x80000003:
8576 case 0x80000004:
8577 case 0x80000005:
8578 case 0x80000006:
8579 case 0x80000007:
8580 case 0x80000008:
8581 case 0x80000009:
8582 case 0x8000000A:
8583 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8584 break;
8585
8586 default:
8587 /* just ignore */
8588 break;
8589 }
8590 }
8591
8592 mHWData->mMemorySize = data.ulMemorySizeMB;
8593 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8594
8595 // boot order
8596 for (size_t i = 0;
8597 i < RT_ELEMENTS(mHWData->mBootOrder);
8598 i++)
8599 {
8600 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8601 if (it == data.mapBootOrder.end())
8602 mHWData->mBootOrder[i] = DeviceType_Null;
8603 else
8604 mHWData->mBootOrder[i] = it->second;
8605 }
8606
8607 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8608 mHWData->mMonitorCount = data.cMonitors;
8609 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8610 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8611 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8612 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8613 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8614 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8615 mHWData->mFirmwareType = data.firmwareType;
8616 mHWData->mPointingHIDType = data.pointingHIDType;
8617 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8618 mHWData->mChipsetType = data.chipsetType;
8619 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8620 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8621 mHWData->mHPETEnabled = data.fHPETEnabled;
8622
8623 /* VRDEServer */
8624 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8625 if (FAILED(rc)) return rc;
8626
8627 /* BIOS */
8628 rc = mBIOSSettings->loadSettings(data.biosSettings);
8629 if (FAILED(rc)) return rc;
8630
8631 // Bandwidth control (must come before network adapters)
8632 rc = mBandwidthControl->loadSettings(data.ioSettings);
8633 if (FAILED(rc)) return rc;
8634
8635 /* USB Controller */
8636 rc = mUSBController->loadSettings(data.usbController);
8637 if (FAILED(rc)) return rc;
8638
8639 // network adapters
8640 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8641 uint32_t oldCount = mNetworkAdapters.size();
8642 if (newCount > oldCount)
8643 {
8644 mNetworkAdapters.resize(newCount);
8645 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8646 {
8647 unconst(mNetworkAdapters[slot]).createObject();
8648 mNetworkAdapters[slot]->init(this, slot);
8649 }
8650 }
8651 else if (newCount < oldCount)
8652 mNetworkAdapters.resize(newCount);
8653 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8654 it != data.llNetworkAdapters.end();
8655 ++it)
8656 {
8657 const settings::NetworkAdapter &nic = *it;
8658
8659 /* slot unicity is guaranteed by XML Schema */
8660 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8661 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8662 if (FAILED(rc)) return rc;
8663 }
8664
8665 // serial ports
8666 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8667 it != data.llSerialPorts.end();
8668 ++it)
8669 {
8670 const settings::SerialPort &s = *it;
8671
8672 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8673 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8674 if (FAILED(rc)) return rc;
8675 }
8676
8677 // parallel ports (optional)
8678 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8679 it != data.llParallelPorts.end();
8680 ++it)
8681 {
8682 const settings::ParallelPort &p = *it;
8683
8684 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8685 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8686 if (FAILED(rc)) return rc;
8687 }
8688
8689 /* AudioAdapter */
8690 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8691 if (FAILED(rc)) return rc;
8692
8693 /* Shared folders */
8694 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8695 it != data.llSharedFolders.end();
8696 ++it)
8697 {
8698 const settings::SharedFolder &sf = *it;
8699
8700 ComObjPtr<SharedFolder> sharedFolder;
8701 /* Check for double entries. Not allowed! */
8702 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8703 if (SUCCEEDED(rc))
8704 return setError(VBOX_E_OBJECT_IN_USE,
8705 tr("Shared folder named '%s' already exists"),
8706 sf.strName.c_str());
8707
8708 /* Create the new shared folder. Don't break on error. This will be
8709 * reported when the machine starts. */
8710 sharedFolder.createObject();
8711 rc = sharedFolder->init(getMachine(),
8712 sf.strName,
8713 sf.strHostPath,
8714 RT_BOOL(sf.fWritable),
8715 RT_BOOL(sf.fAutoMount),
8716 false /* fFailOnError */);
8717 if (FAILED(rc)) return rc;
8718 mHWData->mSharedFolders.push_back(sharedFolder);
8719 }
8720
8721 // Clipboard
8722 mHWData->mClipboardMode = data.clipboardMode;
8723
8724 // drag'n'drop
8725 mHWData->mDragAndDropMode = data.dragAndDropMode;
8726
8727 // guest settings
8728 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8729
8730 // IO settings
8731 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8732 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8733
8734 // Host PCI devices
8735 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8736 it != data.pciAttachments.end();
8737 ++it)
8738 {
8739 const settings::HostPCIDeviceAttachment &hpda = *it;
8740 ComObjPtr<PCIDeviceAttachment> pda;
8741
8742 pda.createObject();
8743 pda->loadSettings(this, hpda);
8744 mHWData->mPCIDeviceAssignments.push_back(pda);
8745 }
8746
8747 /*
8748 * (The following isn't really real hardware, but it lives in HWData
8749 * for reasons of convenience.)
8750 */
8751
8752#ifdef VBOX_WITH_GUEST_PROPS
8753 /* Guest properties (optional) */
8754 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8755 it != data.llGuestProperties.end();
8756 ++it)
8757 {
8758 const settings::GuestProperty &prop = *it;
8759 uint32_t fFlags = guestProp::NILFLAG;
8760 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8761 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8762 mHWData->mGuestProperties[prop.strName] = property;
8763 }
8764
8765 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8766#endif /* VBOX_WITH_GUEST_PROPS defined */
8767
8768 rc = loadDebugging(pDbg);
8769 if (FAILED(rc))
8770 return rc;
8771
8772 mHWData->mAutostart = *pAutostart;
8773
8774 /* default frontend */
8775 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8776 }
8777 catch(std::bad_alloc &)
8778 {
8779 return E_OUTOFMEMORY;
8780 }
8781
8782 AssertComRC(rc);
8783 return rc;
8784}
8785
8786/**
8787 * Called from Machine::loadHardware() to load the debugging settings of the
8788 * machine.
8789 *
8790 * @param pDbg Pointer to the settings.
8791 */
8792HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8793{
8794 mHWData->mDebugging = *pDbg;
8795 /* no more processing currently required, this will probably change. */
8796 return S_OK;
8797}
8798
8799/**
8800 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8801 *
8802 * @param data
8803 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8804 * @param puuidSnapshot
8805 * @return
8806 */
8807HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8808 const Guid *puuidRegistry,
8809 const Guid *puuidSnapshot)
8810{
8811 AssertReturn(!isSessionMachine(), E_FAIL);
8812
8813 HRESULT rc = S_OK;
8814
8815 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8816 it != data.llStorageControllers.end();
8817 ++it)
8818 {
8819 const settings::StorageController &ctlData = *it;
8820
8821 ComObjPtr<StorageController> pCtl;
8822 /* Try to find one with the name first. */
8823 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8824 if (SUCCEEDED(rc))
8825 return setError(VBOX_E_OBJECT_IN_USE,
8826 tr("Storage controller named '%s' already exists"),
8827 ctlData.strName.c_str());
8828
8829 pCtl.createObject();
8830 rc = pCtl->init(this,
8831 ctlData.strName,
8832 ctlData.storageBus,
8833 ctlData.ulInstance,
8834 ctlData.fBootable);
8835 if (FAILED(rc)) return rc;
8836
8837 mStorageControllers->push_back(pCtl);
8838
8839 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8840 if (FAILED(rc)) return rc;
8841
8842 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8843 if (FAILED(rc)) return rc;
8844
8845 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8846 if (FAILED(rc)) return rc;
8847
8848 /* Set IDE emulation settings (only for AHCI controller). */
8849 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8850 {
8851 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8852 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8853 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8854 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8855 )
8856 return rc;
8857 }
8858
8859 /* Load the attached devices now. */
8860 rc = loadStorageDevices(pCtl,
8861 ctlData,
8862 puuidRegistry,
8863 puuidSnapshot);
8864 if (FAILED(rc)) return rc;
8865 }
8866
8867 return S_OK;
8868}
8869
8870/**
8871 * Called from loadStorageControllers for a controller's devices.
8872 *
8873 * @param aStorageController
8874 * @param data
8875 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8876 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8877 * @return
8878 */
8879HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8880 const settings::StorageController &data,
8881 const Guid *puuidRegistry,
8882 const Guid *puuidSnapshot)
8883{
8884 HRESULT rc = S_OK;
8885
8886 /* paranoia: detect duplicate attachments */
8887 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8888 it != data.llAttachedDevices.end();
8889 ++it)
8890 {
8891 const settings::AttachedDevice &ad = *it;
8892
8893 for (settings::AttachedDevicesList::const_iterator it2 = it;
8894 it2 != data.llAttachedDevices.end();
8895 ++it2)
8896 {
8897 if (it == it2)
8898 continue;
8899
8900 const settings::AttachedDevice &ad2 = *it2;
8901
8902 if ( ad.lPort == ad2.lPort
8903 && ad.lDevice == ad2.lDevice)
8904 {
8905 return setError(E_FAIL,
8906 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8907 aStorageController->getName().c_str(),
8908 ad.lPort,
8909 ad.lDevice,
8910 mUserData->s.strName.c_str());
8911 }
8912 }
8913 }
8914
8915 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8916 it != data.llAttachedDevices.end();
8917 ++it)
8918 {
8919 const settings::AttachedDevice &dev = *it;
8920 ComObjPtr<Medium> medium;
8921
8922 switch (dev.deviceType)
8923 {
8924 case DeviceType_Floppy:
8925 case DeviceType_DVD:
8926 if (dev.strHostDriveSrc.isNotEmpty())
8927 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8928 else
8929 rc = mParent->findRemoveableMedium(dev.deviceType,
8930 dev.uuid,
8931 false /* fRefresh */,
8932 false /* aSetError */,
8933 medium);
8934 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8935 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8936 rc = S_OK;
8937 break;
8938
8939 case DeviceType_HardDisk:
8940 {
8941 /* find a hard disk by UUID */
8942 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8943 if (FAILED(rc))
8944 {
8945 if (isSnapshotMachine())
8946 {
8947 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8948 // so the user knows that the bad disk is in a snapshot somewhere
8949 com::ErrorInfo info;
8950 return setError(E_FAIL,
8951 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8952 puuidSnapshot->raw(),
8953 info.getText().raw());
8954 }
8955 else
8956 return rc;
8957 }
8958
8959 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8960
8961 if (medium->getType() == MediumType_Immutable)
8962 {
8963 if (isSnapshotMachine())
8964 return setError(E_FAIL,
8965 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8966 "of the virtual machine '%s' ('%s')"),
8967 medium->getLocationFull().c_str(),
8968 dev.uuid.raw(),
8969 puuidSnapshot->raw(),
8970 mUserData->s.strName.c_str(),
8971 mData->m_strConfigFileFull.c_str());
8972
8973 return setError(E_FAIL,
8974 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8975 medium->getLocationFull().c_str(),
8976 dev.uuid.raw(),
8977 mUserData->s.strName.c_str(),
8978 mData->m_strConfigFileFull.c_str());
8979 }
8980
8981 if (medium->getType() == MediumType_MultiAttach)
8982 {
8983 if (isSnapshotMachine())
8984 return setError(E_FAIL,
8985 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8986 "of the virtual machine '%s' ('%s')"),
8987 medium->getLocationFull().c_str(),
8988 dev.uuid.raw(),
8989 puuidSnapshot->raw(),
8990 mUserData->s.strName.c_str(),
8991 mData->m_strConfigFileFull.c_str());
8992
8993 return setError(E_FAIL,
8994 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8995 medium->getLocationFull().c_str(),
8996 dev.uuid.raw(),
8997 mUserData->s.strName.c_str(),
8998 mData->m_strConfigFileFull.c_str());
8999 }
9000
9001 if ( !isSnapshotMachine()
9002 && medium->getChildren().size() != 0
9003 )
9004 return setError(E_FAIL,
9005 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9006 "because it has %d differencing child hard disks"),
9007 medium->getLocationFull().c_str(),
9008 dev.uuid.raw(),
9009 mUserData->s.strName.c_str(),
9010 mData->m_strConfigFileFull.c_str(),
9011 medium->getChildren().size());
9012
9013 if (findAttachment(mMediaData->mAttachments,
9014 medium))
9015 return setError(E_FAIL,
9016 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9017 medium->getLocationFull().c_str(),
9018 dev.uuid.raw(),
9019 mUserData->s.strName.c_str(),
9020 mData->m_strConfigFileFull.c_str());
9021
9022 break;
9023 }
9024
9025 default:
9026 return setError(E_FAIL,
9027 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9028 medium->getLocationFull().c_str(),
9029 mUserData->s.strName.c_str(),
9030 mData->m_strConfigFileFull.c_str());
9031 }
9032
9033 if (FAILED(rc))
9034 break;
9035
9036 /* Bandwidth groups are loaded at this point. */
9037 ComObjPtr<BandwidthGroup> pBwGroup;
9038
9039 if (!dev.strBwGroup.isEmpty())
9040 {
9041 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9042 if (FAILED(rc))
9043 return setError(E_FAIL,
9044 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9045 medium->getLocationFull().c_str(),
9046 dev.strBwGroup.c_str(),
9047 mUserData->s.strName.c_str(),
9048 mData->m_strConfigFileFull.c_str());
9049 pBwGroup->reference();
9050 }
9051
9052 const Bstr controllerName = aStorageController->getName();
9053 ComObjPtr<MediumAttachment> pAttachment;
9054 pAttachment.createObject();
9055 rc = pAttachment->init(this,
9056 medium,
9057 controllerName,
9058 dev.lPort,
9059 dev.lDevice,
9060 dev.deviceType,
9061 false,
9062 dev.fPassThrough,
9063 dev.fTempEject,
9064 dev.fNonRotational,
9065 dev.fDiscard,
9066 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9067 if (FAILED(rc)) break;
9068
9069 /* associate the medium with this machine and snapshot */
9070 if (!medium.isNull())
9071 {
9072 AutoCaller medCaller(medium);
9073 if (FAILED(medCaller.rc())) return medCaller.rc();
9074 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9075
9076 if (isSnapshotMachine())
9077 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9078 else
9079 rc = medium->addBackReference(mData->mUuid);
9080 /* If the medium->addBackReference fails it sets an appropriate
9081 * error message, so no need to do any guesswork here. */
9082
9083 if (puuidRegistry)
9084 // caller wants registry ID to be set on all attached media (OVF import case)
9085 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9086 }
9087
9088 if (FAILED(rc))
9089 break;
9090
9091 /* back up mMediaData to let registeredInit() properly rollback on failure
9092 * (= limited accessibility) */
9093 setModified(IsModified_Storage);
9094 mMediaData.backup();
9095 mMediaData->mAttachments.push_back(pAttachment);
9096 }
9097
9098 return rc;
9099}
9100
9101/**
9102 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9103 *
9104 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9105 * @param aSnapshot where to return the found snapshot
9106 * @param aSetError true to set extended error info on failure
9107 */
9108HRESULT Machine::findSnapshotById(const Guid &aId,
9109 ComObjPtr<Snapshot> &aSnapshot,
9110 bool aSetError /* = false */)
9111{
9112 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9113
9114 if (!mData->mFirstSnapshot)
9115 {
9116 if (aSetError)
9117 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9118 return E_FAIL;
9119 }
9120
9121 if (aId.isZero())
9122 aSnapshot = mData->mFirstSnapshot;
9123 else
9124 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9125
9126 if (!aSnapshot)
9127 {
9128 if (aSetError)
9129 return setError(E_FAIL,
9130 tr("Could not find a snapshot with UUID {%s}"),
9131 aId.toString().c_str());
9132 return E_FAIL;
9133 }
9134
9135 return S_OK;
9136}
9137
9138/**
9139 * Returns the snapshot with the given name or fails of no such snapshot.
9140 *
9141 * @param aName snapshot name to find
9142 * @param aSnapshot where to return the found snapshot
9143 * @param aSetError true to set extended error info on failure
9144 */
9145HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9146 ComObjPtr<Snapshot> &aSnapshot,
9147 bool aSetError /* = false */)
9148{
9149 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9150
9151 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9152
9153 if (!mData->mFirstSnapshot)
9154 {
9155 if (aSetError)
9156 return setError(VBOX_E_OBJECT_NOT_FOUND,
9157 tr("This machine does not have any snapshots"));
9158 return VBOX_E_OBJECT_NOT_FOUND;
9159 }
9160
9161 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9162
9163 if (!aSnapshot)
9164 {
9165 if (aSetError)
9166 return setError(VBOX_E_OBJECT_NOT_FOUND,
9167 tr("Could not find a snapshot named '%s'"), strName.c_str());
9168 return VBOX_E_OBJECT_NOT_FOUND;
9169 }
9170
9171 return S_OK;
9172}
9173
9174/**
9175 * Returns a storage controller object with the given name.
9176 *
9177 * @param aName storage controller name to find
9178 * @param aStorageController where to return the found storage controller
9179 * @param aSetError true to set extended error info on failure
9180 */
9181HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9182 ComObjPtr<StorageController> &aStorageController,
9183 bool aSetError /* = false */)
9184{
9185 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9186
9187 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9188 it != mStorageControllers->end();
9189 ++it)
9190 {
9191 if ((*it)->getName() == aName)
9192 {
9193 aStorageController = (*it);
9194 return S_OK;
9195 }
9196 }
9197
9198 if (aSetError)
9199 return setError(VBOX_E_OBJECT_NOT_FOUND,
9200 tr("Could not find a storage controller named '%s'"),
9201 aName.c_str());
9202 return VBOX_E_OBJECT_NOT_FOUND;
9203}
9204
9205HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9206 MediaData::AttachmentList &atts)
9207{
9208 AutoCaller autoCaller(this);
9209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9210
9211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9212
9213 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9214 it != mMediaData->mAttachments.end();
9215 ++it)
9216 {
9217 const ComObjPtr<MediumAttachment> &pAtt = *it;
9218
9219 // should never happen, but deal with NULL pointers in the list.
9220 AssertStmt(!pAtt.isNull(), continue);
9221
9222 // getControllerName() needs caller+read lock
9223 AutoCaller autoAttCaller(pAtt);
9224 if (FAILED(autoAttCaller.rc()))
9225 {
9226 atts.clear();
9227 return autoAttCaller.rc();
9228 }
9229 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9230
9231 if (pAtt->getControllerName() == aName)
9232 atts.push_back(pAtt);
9233 }
9234
9235 return S_OK;
9236}
9237
9238/**
9239 * Helper for #saveSettings. Cares about renaming the settings directory and
9240 * file if the machine name was changed and about creating a new settings file
9241 * if this is a new machine.
9242 *
9243 * @note Must be never called directly but only from #saveSettings().
9244 */
9245HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9246{
9247 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9248
9249 HRESULT rc = S_OK;
9250
9251 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9252
9253 /// @todo need to handle primary group change, too
9254
9255 /* attempt to rename the settings file if machine name is changed */
9256 if ( mUserData->s.fNameSync
9257 && mUserData.isBackedUp()
9258 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9259 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9260 )
9261 {
9262 bool dirRenamed = false;
9263 bool fileRenamed = false;
9264
9265 Utf8Str configFile, newConfigFile;
9266 Utf8Str configFilePrev, newConfigFilePrev;
9267 Utf8Str configDir, newConfigDir;
9268
9269 do
9270 {
9271 int vrc = VINF_SUCCESS;
9272
9273 Utf8Str name = mUserData.backedUpData()->s.strName;
9274 Utf8Str newName = mUserData->s.strName;
9275 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9276 if (group == "/")
9277 group.setNull();
9278 Utf8Str newGroup = mUserData->s.llGroups.front();
9279 if (newGroup == "/")
9280 newGroup.setNull();
9281
9282 configFile = mData->m_strConfigFileFull;
9283
9284 /* first, rename the directory if it matches the group and machine name */
9285 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9286 group.c_str(), RTPATH_DELIMITER, name.c_str());
9287 /** @todo hack, make somehow use of ComposeMachineFilename */
9288 if (mUserData->s.fDirectoryIncludesUUID)
9289 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9290 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9291 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9292 /** @todo hack, make somehow use of ComposeMachineFilename */
9293 if (mUserData->s.fDirectoryIncludesUUID)
9294 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9295 configDir = configFile;
9296 configDir.stripFilename();
9297 newConfigDir = configDir;
9298 if ( configDir.length() >= groupPlusName.length()
9299 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9300 {
9301 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9302 Utf8Str newConfigBaseDir(newConfigDir);
9303 newConfigDir.append(newGroupPlusName);
9304 /* consistency: use \ if appropriate on the platform */
9305 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9306 /* new dir and old dir cannot be equal here because of 'if'
9307 * above and because name != newName */
9308 Assert(configDir != newConfigDir);
9309 if (!fSettingsFileIsNew)
9310 {
9311 /* perform real rename only if the machine is not new */
9312 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9313 if ( vrc == VERR_FILE_NOT_FOUND
9314 || vrc == VERR_PATH_NOT_FOUND)
9315 {
9316 /* create the parent directory, then retry renaming */
9317 Utf8Str parent(newConfigDir);
9318 parent.stripFilename();
9319 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9320 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9321 }
9322 if (RT_FAILURE(vrc))
9323 {
9324 rc = setError(E_FAIL,
9325 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9326 configDir.c_str(),
9327 newConfigDir.c_str(),
9328 vrc);
9329 break;
9330 }
9331 /* delete subdirectories which are no longer needed */
9332 Utf8Str dir(configDir);
9333 dir.stripFilename();
9334 while (dir != newConfigBaseDir && dir != ".")
9335 {
9336 vrc = RTDirRemove(dir.c_str());
9337 if (RT_FAILURE(vrc))
9338 break;
9339 dir.stripFilename();
9340 }
9341 dirRenamed = true;
9342 }
9343 }
9344
9345 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9346 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9347
9348 /* then try to rename the settings file itself */
9349 if (newConfigFile != configFile)
9350 {
9351 /* get the path to old settings file in renamed directory */
9352 configFile = Utf8StrFmt("%s%c%s",
9353 newConfigDir.c_str(),
9354 RTPATH_DELIMITER,
9355 RTPathFilename(configFile.c_str()));
9356 if (!fSettingsFileIsNew)
9357 {
9358 /* perform real rename only if the machine is not new */
9359 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9360 if (RT_FAILURE(vrc))
9361 {
9362 rc = setError(E_FAIL,
9363 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9364 configFile.c_str(),
9365 newConfigFile.c_str(),
9366 vrc);
9367 break;
9368 }
9369 fileRenamed = true;
9370 configFilePrev = configFile;
9371 configFilePrev += "-prev";
9372 newConfigFilePrev = newConfigFile;
9373 newConfigFilePrev += "-prev";
9374 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9375 }
9376 }
9377
9378 // update m_strConfigFileFull amd mConfigFile
9379 mData->m_strConfigFileFull = newConfigFile;
9380 // compute the relative path too
9381 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9382
9383 // store the old and new so that VirtualBox::saveSettings() can update
9384 // the media registry
9385 if ( mData->mRegistered
9386 && configDir != newConfigDir)
9387 {
9388 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9389
9390 if (pfNeedsGlobalSaveSettings)
9391 *pfNeedsGlobalSaveSettings = true;
9392 }
9393
9394 // in the saved state file path, replace the old directory with the new directory
9395 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9396 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9397
9398 // and do the same thing for the saved state file paths of all the online snapshots
9399 if (mData->mFirstSnapshot)
9400 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9401 newConfigDir.c_str());
9402 }
9403 while (0);
9404
9405 if (FAILED(rc))
9406 {
9407 /* silently try to rename everything back */
9408 if (fileRenamed)
9409 {
9410 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9411 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9412 }
9413 if (dirRenamed)
9414 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9415 }
9416
9417 if (FAILED(rc)) return rc;
9418 }
9419
9420 if (fSettingsFileIsNew)
9421 {
9422 /* create a virgin config file */
9423 int vrc = VINF_SUCCESS;
9424
9425 /* ensure the settings directory exists */
9426 Utf8Str path(mData->m_strConfigFileFull);
9427 path.stripFilename();
9428 if (!RTDirExists(path.c_str()))
9429 {
9430 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9431 if (RT_FAILURE(vrc))
9432 {
9433 return setError(E_FAIL,
9434 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9435 path.c_str(),
9436 vrc);
9437 }
9438 }
9439
9440 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9441 path = Utf8Str(mData->m_strConfigFileFull);
9442 RTFILE f = NIL_RTFILE;
9443 vrc = RTFileOpen(&f, path.c_str(),
9444 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9445 if (RT_FAILURE(vrc))
9446 return setError(E_FAIL,
9447 tr("Could not create the settings file '%s' (%Rrc)"),
9448 path.c_str(),
9449 vrc);
9450 RTFileClose(f);
9451 }
9452
9453 return rc;
9454}
9455
9456/**
9457 * Saves and commits machine data, user data and hardware data.
9458 *
9459 * Note that on failure, the data remains uncommitted.
9460 *
9461 * @a aFlags may combine the following flags:
9462 *
9463 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9464 * Used when saving settings after an operation that makes them 100%
9465 * correspond to the settings from the current snapshot.
9466 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9467 * #isReallyModified() returns false. This is necessary for cases when we
9468 * change machine data directly, not through the backup()/commit() mechanism.
9469 * - SaveS_Force: settings will be saved without doing a deep compare of the
9470 * settings structures. This is used when this is called because snapshots
9471 * have changed to avoid the overhead of the deep compare.
9472 *
9473 * @note Must be called from under this object's write lock. Locks children for
9474 * writing.
9475 *
9476 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9477 * initialized to false and that will be set to true by this function if
9478 * the caller must invoke VirtualBox::saveSettings() because the global
9479 * settings have changed. This will happen if a machine rename has been
9480 * saved and the global machine and media registries will therefore need
9481 * updating.
9482 */
9483HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9484 int aFlags /*= 0*/)
9485{
9486 LogFlowThisFuncEnter();
9487
9488 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9489
9490 /* make sure child objects are unable to modify the settings while we are
9491 * saving them */
9492 ensureNoStateDependencies();
9493
9494 AssertReturn(!isSnapshotMachine(),
9495 E_FAIL);
9496
9497 HRESULT rc = S_OK;
9498 bool fNeedsWrite = false;
9499
9500 /* First, prepare to save settings. It will care about renaming the
9501 * settings directory and file if the machine name was changed and about
9502 * creating a new settings file if this is a new machine. */
9503 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9504 if (FAILED(rc)) return rc;
9505
9506 // keep a pointer to the current settings structures
9507 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9508 settings::MachineConfigFile *pNewConfig = NULL;
9509
9510 try
9511 {
9512 // make a fresh one to have everyone write stuff into
9513 pNewConfig = new settings::MachineConfigFile(NULL);
9514 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9515
9516 // now go and copy all the settings data from COM to the settings structures
9517 // (this calles saveSettings() on all the COM objects in the machine)
9518 copyMachineDataToSettings(*pNewConfig);
9519
9520 if (aFlags & SaveS_ResetCurStateModified)
9521 {
9522 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9523 mData->mCurrentStateModified = FALSE;
9524 fNeedsWrite = true; // always, no need to compare
9525 }
9526 else if (aFlags & SaveS_Force)
9527 {
9528 fNeedsWrite = true; // always, no need to compare
9529 }
9530 else
9531 {
9532 if (!mData->mCurrentStateModified)
9533 {
9534 // do a deep compare of the settings that we just saved with the settings
9535 // previously stored in the config file; this invokes MachineConfigFile::operator==
9536 // which does a deep compare of all the settings, which is expensive but less expensive
9537 // than writing out XML in vain
9538 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9539
9540 // could still be modified if any settings changed
9541 mData->mCurrentStateModified = fAnySettingsChanged;
9542
9543 fNeedsWrite = fAnySettingsChanged;
9544 }
9545 else
9546 fNeedsWrite = true;
9547 }
9548
9549 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9550
9551 if (fNeedsWrite)
9552 // now spit it all out!
9553 pNewConfig->write(mData->m_strConfigFileFull);
9554
9555 mData->pMachineConfigFile = pNewConfig;
9556 delete pOldConfig;
9557 commit();
9558
9559 // after saving settings, we are no longer different from the XML on disk
9560 mData->flModifications = 0;
9561 }
9562 catch (HRESULT err)
9563 {
9564 // we assume that error info is set by the thrower
9565 rc = err;
9566
9567 // restore old config
9568 delete pNewConfig;
9569 mData->pMachineConfigFile = pOldConfig;
9570 }
9571 catch (...)
9572 {
9573 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9574 }
9575
9576 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9577 {
9578 /* Fire the data change event, even on failure (since we've already
9579 * committed all data). This is done only for SessionMachines because
9580 * mutable Machine instances are always not registered (i.e. private
9581 * to the client process that creates them) and thus don't need to
9582 * inform callbacks. */
9583 if (isSessionMachine())
9584 mParent->onMachineDataChange(mData->mUuid);
9585 }
9586
9587 LogFlowThisFunc(("rc=%08X\n", rc));
9588 LogFlowThisFuncLeave();
9589 return rc;
9590}
9591
9592/**
9593 * Implementation for saving the machine settings into the given
9594 * settings::MachineConfigFile instance. This copies machine extradata
9595 * from the previous machine config file in the instance data, if any.
9596 *
9597 * This gets called from two locations:
9598 *
9599 * -- Machine::saveSettings(), during the regular XML writing;
9600 *
9601 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9602 * exported to OVF and we write the VirtualBox proprietary XML
9603 * into a <vbox:Machine> tag.
9604 *
9605 * This routine fills all the fields in there, including snapshots, *except*
9606 * for the following:
9607 *
9608 * -- fCurrentStateModified. There is some special logic associated with that.
9609 *
9610 * The caller can then call MachineConfigFile::write() or do something else
9611 * with it.
9612 *
9613 * Caller must hold the machine lock!
9614 *
9615 * This throws XML errors and HRESULT, so the caller must have a catch block!
9616 */
9617void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9618{
9619 // deep copy extradata
9620 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9621
9622 config.uuid = mData->mUuid;
9623
9624 // copy name, description, OS type, teleport, UTC etc.
9625 config.machineUserData = mUserData->s;
9626
9627 if ( mData->mMachineState == MachineState_Saved
9628 || mData->mMachineState == MachineState_Restoring
9629 // when deleting a snapshot we may or may not have a saved state in the current state,
9630 // so let's not assert here please
9631 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9632 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9633 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9634 && (!mSSData->strStateFilePath.isEmpty())
9635 )
9636 )
9637 {
9638 Assert(!mSSData->strStateFilePath.isEmpty());
9639 /* try to make the file name relative to the settings file dir */
9640 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9641 }
9642 else
9643 {
9644 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9645 config.strStateFile.setNull();
9646 }
9647
9648 if (mData->mCurrentSnapshot)
9649 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9650 else
9651 config.uuidCurrentSnapshot.clear();
9652
9653 config.timeLastStateChange = mData->mLastStateChange;
9654 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9655 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9656
9657 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9658 if (FAILED(rc)) throw rc;
9659
9660 rc = saveStorageControllers(config.storageMachine);
9661 if (FAILED(rc)) throw rc;
9662
9663 // save machine's media registry if this is VirtualBox 4.0 or later
9664 if (config.canHaveOwnMediaRegistry())
9665 {
9666 // determine machine folder
9667 Utf8Str strMachineFolder = getSettingsFileFull();
9668 strMachineFolder.stripFilename();
9669 mParent->saveMediaRegistry(config.mediaRegistry,
9670 getId(), // only media with registry ID == machine UUID
9671 strMachineFolder);
9672 // this throws HRESULT
9673 }
9674
9675 // save snapshots
9676 rc = saveAllSnapshots(config);
9677 if (FAILED(rc)) throw rc;
9678}
9679
9680/**
9681 * Saves all snapshots of the machine into the given machine config file. Called
9682 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9683 * @param config
9684 * @return
9685 */
9686HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9687{
9688 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9689
9690 HRESULT rc = S_OK;
9691
9692 try
9693 {
9694 config.llFirstSnapshot.clear();
9695
9696 if (mData->mFirstSnapshot)
9697 {
9698 settings::Snapshot snapNew;
9699 config.llFirstSnapshot.push_back(snapNew);
9700
9701 // get reference to the fresh copy of the snapshot on the list and
9702 // work on that copy directly to avoid excessive copying later
9703 settings::Snapshot &snap = config.llFirstSnapshot.front();
9704
9705 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9706 if (FAILED(rc)) throw rc;
9707 }
9708
9709// if (mType == IsSessionMachine)
9710// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9711
9712 }
9713 catch (HRESULT err)
9714 {
9715 /* we assume that error info is set by the thrower */
9716 rc = err;
9717 }
9718 catch (...)
9719 {
9720 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9721 }
9722
9723 return rc;
9724}
9725
9726/**
9727 * Saves the VM hardware configuration. It is assumed that the
9728 * given node is empty.
9729 *
9730 * @param data Reference to the settings object for the hardware config.
9731 * @param pDbg Pointer to the settings object for the debugging config
9732 * which happens to live in mHWData.
9733 * @param pAutostart Pointer to the settings object for the autostart config
9734 * which happens to live in mHWData.
9735 */
9736HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9737 settings::Autostart *pAutostart)
9738{
9739 HRESULT rc = S_OK;
9740
9741 try
9742 {
9743 /* The hardware version attribute (optional).
9744 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9745 if ( mHWData->mHWVersion == "1"
9746 && mSSData->strStateFilePath.isEmpty()
9747 )
9748 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. */
9749
9750 data.strVersion = mHWData->mHWVersion;
9751 data.uuid = mHWData->mHardwareUUID;
9752
9753 // CPU
9754 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9755 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9756 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9757 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9758 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9759 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9760 data.fPAE = !!mHWData->mPAEEnabled;
9761 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9762
9763 /* Standard and Extended CPUID leafs. */
9764 data.llCpuIdLeafs.clear();
9765 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9766 {
9767 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9768 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9769 }
9770 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9771 {
9772 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9773 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9774 }
9775
9776 data.cCPUs = mHWData->mCPUCount;
9777 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9778 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9779
9780 data.llCpus.clear();
9781 if (data.fCpuHotPlug)
9782 {
9783 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9784 {
9785 if (mHWData->mCPUAttached[idx])
9786 {
9787 settings::Cpu cpu;
9788 cpu.ulId = idx;
9789 data.llCpus.push_back(cpu);
9790 }
9791 }
9792 }
9793
9794 // memory
9795 data.ulMemorySizeMB = mHWData->mMemorySize;
9796 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9797
9798 // firmware
9799 data.firmwareType = mHWData->mFirmwareType;
9800
9801 // HID
9802 data.pointingHIDType = mHWData->mPointingHIDType;
9803 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9804
9805 // chipset
9806 data.chipsetType = mHWData->mChipsetType;
9807
9808 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9809 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9810
9811 // HPET
9812 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9813
9814 // boot order
9815 data.mapBootOrder.clear();
9816 for (size_t i = 0;
9817 i < RT_ELEMENTS(mHWData->mBootOrder);
9818 ++i)
9819 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9820
9821 // display
9822 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9823 data.cMonitors = mHWData->mMonitorCount;
9824 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9825 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9826 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9827 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9828 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9829 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9830
9831 /* VRDEServer settings (optional) */
9832 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9833 if (FAILED(rc)) throw rc;
9834
9835 /* BIOS (required) */
9836 rc = mBIOSSettings->saveSettings(data.biosSettings);
9837 if (FAILED(rc)) throw rc;
9838
9839 /* USB Controller (required) */
9840 rc = mUSBController->saveSettings(data.usbController);
9841 if (FAILED(rc)) throw rc;
9842
9843 /* Network adapters (required) */
9844 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9845 data.llNetworkAdapters.clear();
9846 /* Write out only the nominal number of network adapters for this
9847 * chipset type. Since Machine::commit() hasn't been called there
9848 * may be extra NIC settings in the vector. */
9849 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9850 {
9851 settings::NetworkAdapter nic;
9852 nic.ulSlot = slot;
9853 /* paranoia check... must not be NULL, but must not crash either. */
9854 if (mNetworkAdapters[slot])
9855 {
9856 rc = mNetworkAdapters[slot]->saveSettings(nic);
9857 if (FAILED(rc)) throw rc;
9858
9859 data.llNetworkAdapters.push_back(nic);
9860 }
9861 }
9862
9863 /* Serial ports */
9864 data.llSerialPorts.clear();
9865 for (ULONG slot = 0;
9866 slot < RT_ELEMENTS(mSerialPorts);
9867 ++slot)
9868 {
9869 settings::SerialPort s;
9870 s.ulSlot = slot;
9871 rc = mSerialPorts[slot]->saveSettings(s);
9872 if (FAILED(rc)) return rc;
9873
9874 data.llSerialPorts.push_back(s);
9875 }
9876
9877 /* Parallel ports */
9878 data.llParallelPorts.clear();
9879 for (ULONG slot = 0;
9880 slot < RT_ELEMENTS(mParallelPorts);
9881 ++slot)
9882 {
9883 settings::ParallelPort p;
9884 p.ulSlot = slot;
9885 rc = mParallelPorts[slot]->saveSettings(p);
9886 if (FAILED(rc)) return rc;
9887
9888 data.llParallelPorts.push_back(p);
9889 }
9890
9891 /* Audio adapter */
9892 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9893 if (FAILED(rc)) return rc;
9894
9895 /* Shared folders */
9896 data.llSharedFolders.clear();
9897 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9898 it != mHWData->mSharedFolders.end();
9899 ++it)
9900 {
9901 SharedFolder *pSF = *it;
9902 AutoCaller sfCaller(pSF);
9903 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9904 settings::SharedFolder sf;
9905 sf.strName = pSF->getName();
9906 sf.strHostPath = pSF->getHostPath();
9907 sf.fWritable = !!pSF->isWritable();
9908 sf.fAutoMount = !!pSF->isAutoMounted();
9909
9910 data.llSharedFolders.push_back(sf);
9911 }
9912
9913 // clipboard
9914 data.clipboardMode = mHWData->mClipboardMode;
9915
9916 // drag'n'drop
9917 data.dragAndDropMode = mHWData->mDragAndDropMode;
9918
9919 /* Guest */
9920 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9921
9922 // IO settings
9923 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9924 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9925
9926 /* BandwidthControl (required) */
9927 rc = mBandwidthControl->saveSettings(data.ioSettings);
9928 if (FAILED(rc)) throw rc;
9929
9930 /* Host PCI devices */
9931 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9932 it != mHWData->mPCIDeviceAssignments.end();
9933 ++it)
9934 {
9935 ComObjPtr<PCIDeviceAttachment> pda = *it;
9936 settings::HostPCIDeviceAttachment hpda;
9937
9938 rc = pda->saveSettings(hpda);
9939 if (FAILED(rc)) throw rc;
9940
9941 data.pciAttachments.push_back(hpda);
9942 }
9943
9944
9945 // guest properties
9946 data.llGuestProperties.clear();
9947#ifdef VBOX_WITH_GUEST_PROPS
9948 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
9949 it != mHWData->mGuestProperties.end();
9950 ++it)
9951 {
9952 HWData::GuestProperty property = it->second;
9953
9954 /* Remove transient guest properties at shutdown unless we
9955 * are saving state */
9956 if ( ( mData->mMachineState == MachineState_PoweredOff
9957 || mData->mMachineState == MachineState_Aborted
9958 || mData->mMachineState == MachineState_Teleported)
9959 && ( property.mFlags & guestProp::TRANSIENT
9960 || property.mFlags & guestProp::TRANSRESET))
9961 continue;
9962 settings::GuestProperty prop;
9963 prop.strName = it->first;
9964 prop.strValue = property.strValue;
9965 prop.timestamp = property.mTimestamp;
9966 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9967 guestProp::writeFlags(property.mFlags, szFlags);
9968 prop.strFlags = szFlags;
9969
9970 data.llGuestProperties.push_back(prop);
9971 }
9972
9973 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9974 /* I presume this doesn't require a backup(). */
9975 mData->mGuestPropertiesModified = FALSE;
9976#endif /* VBOX_WITH_GUEST_PROPS defined */
9977
9978 *pDbg = mHWData->mDebugging;
9979 *pAutostart = mHWData->mAutostart;
9980
9981 data.strDefaultFrontend = mHWData->mDefaultFrontend;
9982 }
9983 catch(std::bad_alloc &)
9984 {
9985 return E_OUTOFMEMORY;
9986 }
9987
9988 AssertComRC(rc);
9989 return rc;
9990}
9991
9992/**
9993 * Saves the storage controller configuration.
9994 *
9995 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9996 */
9997HRESULT Machine::saveStorageControllers(settings::Storage &data)
9998{
9999 data.llStorageControllers.clear();
10000
10001 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10002 it != mStorageControllers->end();
10003 ++it)
10004 {
10005 HRESULT rc;
10006 ComObjPtr<StorageController> pCtl = *it;
10007
10008 settings::StorageController ctl;
10009 ctl.strName = pCtl->getName();
10010 ctl.controllerType = pCtl->getControllerType();
10011 ctl.storageBus = pCtl->getStorageBus();
10012 ctl.ulInstance = pCtl->getInstance();
10013 ctl.fBootable = pCtl->getBootable();
10014
10015 /* Save the port count. */
10016 ULONG portCount;
10017 rc = pCtl->COMGETTER(PortCount)(&portCount);
10018 ComAssertComRCRet(rc, rc);
10019 ctl.ulPortCount = portCount;
10020
10021 /* Save fUseHostIOCache */
10022 BOOL fUseHostIOCache;
10023 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10024 ComAssertComRCRet(rc, rc);
10025 ctl.fUseHostIOCache = !!fUseHostIOCache;
10026
10027 /* Save IDE emulation settings. */
10028 if (ctl.controllerType == StorageControllerType_IntelAhci)
10029 {
10030 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10031 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10032 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10033 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10034 )
10035 ComAssertComRCRet(rc, rc);
10036 }
10037
10038 /* save the devices now. */
10039 rc = saveStorageDevices(pCtl, ctl);
10040 ComAssertComRCRet(rc, rc);
10041
10042 data.llStorageControllers.push_back(ctl);
10043 }
10044
10045 return S_OK;
10046}
10047
10048/**
10049 * Saves the hard disk configuration.
10050 */
10051HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10052 settings::StorageController &data)
10053{
10054 MediaData::AttachmentList atts;
10055
10056 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10057 if (FAILED(rc)) return rc;
10058
10059 data.llAttachedDevices.clear();
10060 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10061 it != atts.end();
10062 ++it)
10063 {
10064 settings::AttachedDevice dev;
10065
10066 MediumAttachment *pAttach = *it;
10067 Medium *pMedium = pAttach->getMedium();
10068
10069 dev.deviceType = pAttach->getType();
10070 dev.lPort = pAttach->getPort();
10071 dev.lDevice = pAttach->getDevice();
10072 if (pMedium)
10073 {
10074 if (pMedium->isHostDrive())
10075 dev.strHostDriveSrc = pMedium->getLocationFull();
10076 else
10077 dev.uuid = pMedium->getId();
10078 dev.fPassThrough = pAttach->getPassthrough();
10079 dev.fTempEject = pAttach->getTempEject();
10080 dev.fNonRotational = pAttach->getNonRotational();
10081 dev.fDiscard = pAttach->getDiscard();
10082 }
10083
10084 dev.strBwGroup = pAttach->getBandwidthGroup();
10085
10086 data.llAttachedDevices.push_back(dev);
10087 }
10088
10089 return S_OK;
10090}
10091
10092/**
10093 * Saves machine state settings as defined by aFlags
10094 * (SaveSTS_* values).
10095 *
10096 * @param aFlags Combination of SaveSTS_* flags.
10097 *
10098 * @note Locks objects for writing.
10099 */
10100HRESULT Machine::saveStateSettings(int aFlags)
10101{
10102 if (aFlags == 0)
10103 return S_OK;
10104
10105 AutoCaller autoCaller(this);
10106 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10107
10108 /* This object's write lock is also necessary to serialize file access
10109 * (prevent concurrent reads and writes) */
10110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10111
10112 HRESULT rc = S_OK;
10113
10114 Assert(mData->pMachineConfigFile);
10115
10116 try
10117 {
10118 if (aFlags & SaveSTS_CurStateModified)
10119 mData->pMachineConfigFile->fCurrentStateModified = true;
10120
10121 if (aFlags & SaveSTS_StateFilePath)
10122 {
10123 if (!mSSData->strStateFilePath.isEmpty())
10124 /* try to make the file name relative to the settings file dir */
10125 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10126 else
10127 mData->pMachineConfigFile->strStateFile.setNull();
10128 }
10129
10130 if (aFlags & SaveSTS_StateTimeStamp)
10131 {
10132 Assert( mData->mMachineState != MachineState_Aborted
10133 || mSSData->strStateFilePath.isEmpty());
10134
10135 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10136
10137 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10138//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10139 }
10140
10141 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10142 }
10143 catch (...)
10144 {
10145 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10146 }
10147
10148 return rc;
10149}
10150
10151/**
10152 * Ensures that the given medium is added to a media registry. If this machine
10153 * was created with 4.0 or later, then the machine registry is used. Otherwise
10154 * the global VirtualBox media registry is used.
10155 *
10156 * Caller must NOT hold machine lock, media tree or any medium locks!
10157 *
10158 * @param pMedium
10159 */
10160void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10161{
10162 /* Paranoia checks: do not hold machine or media tree locks. */
10163 AssertReturnVoid(!isWriteLockOnCurrentThread());
10164 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10165
10166 ComObjPtr<Medium> pBase;
10167 {
10168 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10169 pBase = pMedium->getBase();
10170 }
10171
10172 /* Paranoia checks: do not hold medium locks. */
10173 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10174 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10175
10176 // decide which medium registry to use now that the medium is attached:
10177 Guid uuid;
10178 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10179 // machine XML is VirtualBox 4.0 or higher:
10180 uuid = getId(); // machine UUID
10181 else
10182 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10183
10184 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10185 mParent->markRegistryModified(uuid);
10186
10187 /* For more complex hard disk structures it can happen that the base
10188 * medium isn't yet associated with any medium registry. Do that now. */
10189 if (pMedium != pBase)
10190 {
10191 if (pBase->addRegistry(uuid, true /* fRecurse */))
10192 mParent->markRegistryModified(uuid);
10193 }
10194}
10195
10196/**
10197 * Creates differencing hard disks for all normal hard disks attached to this
10198 * machine and a new set of attachments to refer to created disks.
10199 *
10200 * Used when taking a snapshot or when deleting the current state. Gets called
10201 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10202 *
10203 * This method assumes that mMediaData contains the original hard disk attachments
10204 * it needs to create diffs for. On success, these attachments will be replaced
10205 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10206 * called to delete created diffs which will also rollback mMediaData and restore
10207 * whatever was backed up before calling this method.
10208 *
10209 * Attachments with non-normal hard disks are left as is.
10210 *
10211 * If @a aOnline is @c false then the original hard disks that require implicit
10212 * diffs will be locked for reading. Otherwise it is assumed that they are
10213 * already locked for writing (when the VM was started). Note that in the latter
10214 * case it is responsibility of the caller to lock the newly created diffs for
10215 * writing if this method succeeds.
10216 *
10217 * @param aProgress Progress object to run (must contain at least as
10218 * many operations left as the number of hard disks
10219 * attached).
10220 * @param aOnline Whether the VM was online prior to this operation.
10221 *
10222 * @note The progress object is not marked as completed, neither on success nor
10223 * on failure. This is a responsibility of the caller.
10224 *
10225 * @note Locks this object and the media tree for writing.
10226 */
10227HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10228 ULONG aWeight,
10229 bool aOnline)
10230{
10231 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10232
10233 AutoCaller autoCaller(this);
10234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10235
10236 AutoMultiWriteLock2 alock(this->lockHandle(),
10237 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10238
10239 /* must be in a protective state because we release the lock below */
10240 AssertReturn( mData->mMachineState == MachineState_Saving
10241 || mData->mMachineState == MachineState_LiveSnapshotting
10242 || mData->mMachineState == MachineState_RestoringSnapshot
10243 || mData->mMachineState == MachineState_DeletingSnapshot
10244 , E_FAIL);
10245
10246 HRESULT rc = S_OK;
10247
10248 // use appropriate locked media map (online or offline)
10249 MediumLockListMap lockedMediaOffline;
10250 MediumLockListMap *lockedMediaMap;
10251 if (aOnline)
10252 lockedMediaMap = &mData->mSession.mLockedMedia;
10253 else
10254 lockedMediaMap = &lockedMediaOffline;
10255
10256 try
10257 {
10258 if (!aOnline)
10259 {
10260 /* lock all attached hard disks early to detect "in use"
10261 * situations before creating actual diffs */
10262 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10263 it != mMediaData->mAttachments.end();
10264 ++it)
10265 {
10266 MediumAttachment* pAtt = *it;
10267 if (pAtt->getType() == DeviceType_HardDisk)
10268 {
10269 Medium* pMedium = pAtt->getMedium();
10270 Assert(pMedium);
10271
10272 MediumLockList *pMediumLockList(new MediumLockList());
10273 alock.release();
10274 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10275 false /* fMediumLockWrite */,
10276 NULL,
10277 *pMediumLockList);
10278 alock.acquire();
10279 if (FAILED(rc))
10280 {
10281 delete pMediumLockList;
10282 throw rc;
10283 }
10284 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10285 if (FAILED(rc))
10286 {
10287 throw setError(rc,
10288 tr("Collecting locking information for all attached media failed"));
10289 }
10290 }
10291 }
10292
10293 /* Now lock all media. If this fails, nothing is locked. */
10294 alock.release();
10295 rc = lockedMediaMap->Lock();
10296 alock.acquire();
10297 if (FAILED(rc))
10298 {
10299 throw setError(rc,
10300 tr("Locking of attached media failed"));
10301 }
10302 }
10303
10304 /* remember the current list (note that we don't use backup() since
10305 * mMediaData may be already backed up) */
10306 MediaData::AttachmentList atts = mMediaData->mAttachments;
10307
10308 /* start from scratch */
10309 mMediaData->mAttachments.clear();
10310
10311 /* go through remembered attachments and create diffs for normal hard
10312 * disks and attach them */
10313 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10314 it != atts.end();
10315 ++it)
10316 {
10317 MediumAttachment* pAtt = *it;
10318
10319 DeviceType_T devType = pAtt->getType();
10320 Medium* pMedium = pAtt->getMedium();
10321
10322 if ( devType != DeviceType_HardDisk
10323 || pMedium == NULL
10324 || pMedium->getType() != MediumType_Normal)
10325 {
10326 /* copy the attachment as is */
10327
10328 /** @todo the progress object created in Console::TakeSnaphot
10329 * only expects operations for hard disks. Later other
10330 * device types need to show up in the progress as well. */
10331 if (devType == DeviceType_HardDisk)
10332 {
10333 if (pMedium == NULL)
10334 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10335 aWeight); // weight
10336 else
10337 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10338 pMedium->getBase()->getName().c_str()).raw(),
10339 aWeight); // weight
10340 }
10341
10342 mMediaData->mAttachments.push_back(pAtt);
10343 continue;
10344 }
10345
10346 /* need a diff */
10347 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10348 pMedium->getBase()->getName().c_str()).raw(),
10349 aWeight); // weight
10350
10351 Utf8Str strFullSnapshotFolder;
10352 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10353
10354 ComObjPtr<Medium> diff;
10355 diff.createObject();
10356 // store the diff in the same registry as the parent
10357 // (this cannot fail here because we can't create implicit diffs for
10358 // unregistered images)
10359 Guid uuidRegistryParent;
10360 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10361 Assert(fInRegistry); NOREF(fInRegistry);
10362 rc = diff->init(mParent,
10363 pMedium->getPreferredDiffFormat(),
10364 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10365 uuidRegistryParent);
10366 if (FAILED(rc)) throw rc;
10367
10368 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10369 * the push_back? Looks like we're going to release medium with the
10370 * wrong kind of lock (general issue with if we fail anywhere at all)
10371 * and an orphaned VDI in the snapshots folder. */
10372
10373 /* update the appropriate lock list */
10374 MediumLockList *pMediumLockList;
10375 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10376 AssertComRCThrowRC(rc);
10377 if (aOnline)
10378 {
10379 alock.release();
10380 /* The currently attached medium will be read-only, change
10381 * the lock type to read. */
10382 rc = pMediumLockList->Update(pMedium, false);
10383 alock.acquire();
10384 AssertComRCThrowRC(rc);
10385 }
10386
10387 /* release the locks before the potentially lengthy operation */
10388 alock.release();
10389 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10390 pMediumLockList,
10391 NULL /* aProgress */,
10392 true /* aWait */);
10393 alock.acquire();
10394 if (FAILED(rc)) throw rc;
10395
10396 rc = lockedMediaMap->Unlock();
10397 AssertComRCThrowRC(rc);
10398 alock.release();
10399 rc = pMediumLockList->Append(diff, true);
10400 alock.acquire();
10401 AssertComRCThrowRC(rc);
10402 alock.release();
10403 rc = lockedMediaMap->Lock();
10404 alock.acquire();
10405 AssertComRCThrowRC(rc);
10406
10407 rc = diff->addBackReference(mData->mUuid);
10408 AssertComRCThrowRC(rc);
10409
10410 /* add a new attachment */
10411 ComObjPtr<MediumAttachment> attachment;
10412 attachment.createObject();
10413 rc = attachment->init(this,
10414 diff,
10415 pAtt->getControllerName(),
10416 pAtt->getPort(),
10417 pAtt->getDevice(),
10418 DeviceType_HardDisk,
10419 true /* aImplicit */,
10420 false /* aPassthrough */,
10421 false /* aTempEject */,
10422 pAtt->getNonRotational(),
10423 pAtt->getDiscard(),
10424 pAtt->getBandwidthGroup());
10425 if (FAILED(rc)) throw rc;
10426
10427 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10428 AssertComRCThrowRC(rc);
10429 mMediaData->mAttachments.push_back(attachment);
10430 }
10431 }
10432 catch (HRESULT aRC) { rc = aRC; }
10433
10434 /* unlock all hard disks we locked when there is no VM */
10435 if (!aOnline)
10436 {
10437 ErrorInfoKeeper eik;
10438
10439 HRESULT rc1 = lockedMediaMap->Clear();
10440 AssertComRC(rc1);
10441 }
10442
10443 return rc;
10444}
10445
10446/**
10447 * Deletes implicit differencing hard disks created either by
10448 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10449 *
10450 * Note that to delete hard disks created by #AttachDevice() this method is
10451 * called from #fixupMedia() when the changes are rolled back.
10452 *
10453 * @note Locks this object and the media tree for writing.
10454 */
10455HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10456{
10457 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10458
10459 AutoCaller autoCaller(this);
10460 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10461
10462 AutoMultiWriteLock2 alock(this->lockHandle(),
10463 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10464
10465 /* We absolutely must have backed up state. */
10466 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10467
10468 /* Check if there are any implicitly created diff images. */
10469 bool fImplicitDiffs = false;
10470 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10471 it != mMediaData->mAttachments.end();
10472 ++it)
10473 {
10474 const ComObjPtr<MediumAttachment> &pAtt = *it;
10475 if (pAtt->isImplicit())
10476 {
10477 fImplicitDiffs = true;
10478 break;
10479 }
10480 }
10481 /* If there is nothing to do, leave early. This saves lots of image locking
10482 * effort. It also avoids a MachineStateChanged event without real reason.
10483 * This is important e.g. when loading a VM config, because there should be
10484 * no events. Otherwise API clients can become thoroughly confused for
10485 * inaccessible VMs (the code for loading VM configs uses this method for
10486 * cleanup if the config makes no sense), as they take such events as an
10487 * indication that the VM is alive, and they would force the VM config to
10488 * be reread, leading to an endless loop. */
10489 if (!fImplicitDiffs)
10490 return S_OK;
10491
10492 HRESULT rc = S_OK;
10493 MachineState_T oldState = mData->mMachineState;
10494
10495 /* will release the lock before the potentially lengthy operation,
10496 * so protect with the special state (unless already protected) */
10497 if ( oldState != MachineState_Saving
10498 && oldState != MachineState_LiveSnapshotting
10499 && oldState != MachineState_RestoringSnapshot
10500 && oldState != MachineState_DeletingSnapshot
10501 && oldState != MachineState_DeletingSnapshotOnline
10502 && oldState != MachineState_DeletingSnapshotPaused
10503 )
10504 setMachineState(MachineState_SettingUp);
10505
10506 // use appropriate locked media map (online or offline)
10507 MediumLockListMap lockedMediaOffline;
10508 MediumLockListMap *lockedMediaMap;
10509 if (aOnline)
10510 lockedMediaMap = &mData->mSession.mLockedMedia;
10511 else
10512 lockedMediaMap = &lockedMediaOffline;
10513
10514 try
10515 {
10516 if (!aOnline)
10517 {
10518 /* lock all attached hard disks early to detect "in use"
10519 * situations before deleting actual diffs */
10520 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10521 it != mMediaData->mAttachments.end();
10522 ++it)
10523 {
10524 MediumAttachment* pAtt = *it;
10525 if (pAtt->getType() == DeviceType_HardDisk)
10526 {
10527 Medium* pMedium = pAtt->getMedium();
10528 Assert(pMedium);
10529
10530 MediumLockList *pMediumLockList(new MediumLockList());
10531 alock.release();
10532 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10533 false /* fMediumLockWrite */,
10534 NULL,
10535 *pMediumLockList);
10536 alock.acquire();
10537
10538 if (FAILED(rc))
10539 {
10540 delete pMediumLockList;
10541 throw rc;
10542 }
10543
10544 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10545 if (FAILED(rc))
10546 throw rc;
10547 }
10548 }
10549
10550 if (FAILED(rc))
10551 throw rc;
10552 } // end of offline
10553
10554 /* Lock lists are now up to date and include implicitly created media */
10555
10556 /* Go through remembered attachments and delete all implicitly created
10557 * diffs and fix up the attachment information */
10558 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10559 MediaData::AttachmentList implicitAtts;
10560 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10561 it != mMediaData->mAttachments.end();
10562 ++it)
10563 {
10564 ComObjPtr<MediumAttachment> pAtt = *it;
10565 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10566 if (pMedium.isNull())
10567 continue;
10568
10569 // Implicit attachments go on the list for deletion and back references are removed.
10570 if (pAtt->isImplicit())
10571 {
10572 /* Deassociate and mark for deletion */
10573 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10574 rc = pMedium->removeBackReference(mData->mUuid);
10575 if (FAILED(rc))
10576 throw rc;
10577 implicitAtts.push_back(pAtt);
10578 continue;
10579 }
10580
10581 /* Was this medium attached before? */
10582 if (!findAttachment(oldAtts, pMedium))
10583 {
10584 /* no: de-associate */
10585 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10586 rc = pMedium->removeBackReference(mData->mUuid);
10587 if (FAILED(rc))
10588 throw rc;
10589 continue;
10590 }
10591 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10592 }
10593
10594 /* If there are implicit attachments to delete, throw away the lock
10595 * map contents (which will unlock all media) since the medium
10596 * attachments will be rolled back. Below we need to completely
10597 * recreate the lock map anyway since it is infinitely complex to
10598 * do this incrementally (would need reconstructing each attachment
10599 * change, which would be extremely hairy). */
10600 if (implicitAtts.size() != 0)
10601 {
10602 ErrorInfoKeeper eik;
10603
10604 HRESULT rc1 = lockedMediaMap->Clear();
10605 AssertComRC(rc1);
10606 }
10607
10608 /* rollback hard disk changes */
10609 mMediaData.rollback();
10610
10611 MultiResult mrc(S_OK);
10612
10613 // Delete unused implicit diffs.
10614 if (implicitAtts.size() != 0)
10615 {
10616 alock.release();
10617
10618 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10619 it != implicitAtts.end();
10620 ++it)
10621 {
10622 // Remove medium associated with this attachment.
10623 ComObjPtr<MediumAttachment> pAtt = *it;
10624 Assert(pAtt);
10625 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10626 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10627 Assert(pMedium);
10628
10629 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10630 // continue on delete failure, just collect error messages
10631 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10632 mrc = rc;
10633 }
10634
10635 alock.acquire();
10636
10637 /* if there is a VM recreate media lock map as mentioned above,
10638 * otherwise it is a waste of time and we leave things unlocked */
10639 if (aOnline)
10640 {
10641 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10642 /* must never be NULL, but better safe than sorry */
10643 if (!pMachine.isNull())
10644 {
10645 alock.release();
10646 rc = mData->mSession.mMachine->lockMedia();
10647 alock.acquire();
10648 if (FAILED(rc))
10649 throw rc;
10650 }
10651 }
10652 }
10653 }
10654 catch (HRESULT aRC) {rc = aRC;}
10655
10656 if (mData->mMachineState == MachineState_SettingUp)
10657 setMachineState(oldState);
10658
10659 /* unlock all hard disks we locked when there is no VM */
10660 if (!aOnline)
10661 {
10662 ErrorInfoKeeper eik;
10663
10664 HRESULT rc1 = lockedMediaMap->Clear();
10665 AssertComRC(rc1);
10666 }
10667
10668 return rc;
10669}
10670
10671
10672/**
10673 * Looks through the given list of media attachments for one with the given parameters
10674 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10675 * can be searched as well if needed.
10676 *
10677 * @param list
10678 * @param aControllerName
10679 * @param aControllerPort
10680 * @param aDevice
10681 * @return
10682 */
10683MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10684 IN_BSTR aControllerName,
10685 LONG aControllerPort,
10686 LONG aDevice)
10687{
10688 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10689 it != ll.end();
10690 ++it)
10691 {
10692 MediumAttachment *pAttach = *it;
10693 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10694 return pAttach;
10695 }
10696
10697 return NULL;
10698}
10699
10700/**
10701 * Looks through the given list of media attachments for one with the given parameters
10702 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10703 * can be searched as well if needed.
10704 *
10705 * @param list
10706 * @param aControllerName
10707 * @param aControllerPort
10708 * @param aDevice
10709 * @return
10710 */
10711MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10712 ComObjPtr<Medium> pMedium)
10713{
10714 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10715 it != ll.end();
10716 ++it)
10717 {
10718 MediumAttachment *pAttach = *it;
10719 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10720 if (pMediumThis == pMedium)
10721 return pAttach;
10722 }
10723
10724 return NULL;
10725}
10726
10727/**
10728 * Looks through the given list of media attachments for one with the given parameters
10729 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10730 * can be searched as well if needed.
10731 *
10732 * @param list
10733 * @param aControllerName
10734 * @param aControllerPort
10735 * @param aDevice
10736 * @return
10737 */
10738MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10739 Guid &id)
10740{
10741 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10742 it != ll.end();
10743 ++it)
10744 {
10745 MediumAttachment *pAttach = *it;
10746 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10747 if (pMediumThis->getId() == id)
10748 return pAttach;
10749 }
10750
10751 return NULL;
10752}
10753
10754/**
10755 * Main implementation for Machine::DetachDevice. This also gets called
10756 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10757 *
10758 * @param pAttach Medium attachment to detach.
10759 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10760 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10761 * @return
10762 */
10763HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10764 AutoWriteLock &writeLock,
10765 Snapshot *pSnapshot)
10766{
10767 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10768 DeviceType_T mediumType = pAttach->getType();
10769
10770 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10771
10772 if (pAttach->isImplicit())
10773 {
10774 /* attempt to implicitly delete the implicitly created diff */
10775
10776 /// @todo move the implicit flag from MediumAttachment to Medium
10777 /// and forbid any hard disk operation when it is implicit. Or maybe
10778 /// a special media state for it to make it even more simple.
10779
10780 Assert(mMediaData.isBackedUp());
10781
10782 /* will release the lock before the potentially lengthy operation, so
10783 * protect with the special state */
10784 MachineState_T oldState = mData->mMachineState;
10785 setMachineState(MachineState_SettingUp);
10786
10787 writeLock.release();
10788
10789 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10790 true /*aWait*/);
10791
10792 writeLock.acquire();
10793
10794 setMachineState(oldState);
10795
10796 if (FAILED(rc)) return rc;
10797 }
10798
10799 setModified(IsModified_Storage);
10800 mMediaData.backup();
10801 mMediaData->mAttachments.remove(pAttach);
10802
10803 if (!oldmedium.isNull())
10804 {
10805 // if this is from a snapshot, do not defer detachment to commitMedia()
10806 if (pSnapshot)
10807 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10808 // else if non-hard disk media, do not defer detachment to commitMedia() either
10809 else if (mediumType != DeviceType_HardDisk)
10810 oldmedium->removeBackReference(mData->mUuid);
10811 }
10812
10813 return S_OK;
10814}
10815
10816/**
10817 * Goes thru all media of the given list and
10818 *
10819 * 1) calls detachDevice() on each of them for this machine and
10820 * 2) adds all Medium objects found in the process to the given list,
10821 * depending on cleanupMode.
10822 *
10823 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10824 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10825 * media to the list.
10826 *
10827 * This gets called from Machine::Unregister, both for the actual Machine and
10828 * the SnapshotMachine objects that might be found in the snapshots.
10829 *
10830 * Requires caller and locking. The machine lock must be passed in because it
10831 * will be passed on to detachDevice which needs it for temporary unlocking.
10832 *
10833 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10834 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10835 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10836 * otherwise no media get added.
10837 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10838 * @return
10839 */
10840HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10841 Snapshot *pSnapshot,
10842 CleanupMode_T cleanupMode,
10843 MediaList &llMedia)
10844{
10845 Assert(isWriteLockOnCurrentThread());
10846
10847 HRESULT rc;
10848
10849 // make a temporary list because detachDevice invalidates iterators into
10850 // mMediaData->mAttachments
10851 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10852
10853 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10854 it != llAttachments2.end();
10855 ++it)
10856 {
10857 ComObjPtr<MediumAttachment> &pAttach = *it;
10858 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10859
10860 if (!pMedium.isNull())
10861 {
10862 AutoCaller mac(pMedium);
10863 if (FAILED(mac.rc())) return mac.rc();
10864 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10865 DeviceType_T devType = pMedium->getDeviceType();
10866 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10867 && devType == DeviceType_HardDisk)
10868 || (cleanupMode == CleanupMode_Full)
10869 )
10870 {
10871 llMedia.push_back(pMedium);
10872 ComObjPtr<Medium> pParent = pMedium->getParent();
10873 /*
10874 * Search for medias which are not attached to any machine, but
10875 * in the chain to an attached disk. Mediums are only consided
10876 * if they are:
10877 * - have only one child
10878 * - no references to any machines
10879 * - are of normal medium type
10880 */
10881 while (!pParent.isNull())
10882 {
10883 AutoCaller mac1(pParent);
10884 if (FAILED(mac1.rc())) return mac1.rc();
10885 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10886 if (pParent->getChildren().size() == 1)
10887 {
10888 if ( pParent->getMachineBackRefCount() == 0
10889 && pParent->getType() == MediumType_Normal
10890 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10891 llMedia.push_back(pParent);
10892 }
10893 else
10894 break;
10895 pParent = pParent->getParent();
10896 }
10897 }
10898 }
10899
10900 // real machine: then we need to use the proper method
10901 rc = detachDevice(pAttach, writeLock, pSnapshot);
10902
10903 if (FAILED(rc))
10904 return rc;
10905 }
10906
10907 return S_OK;
10908}
10909
10910/**
10911 * Perform deferred hard disk detachments.
10912 *
10913 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10914 * backed up).
10915 *
10916 * If @a aOnline is @c true then this method will also unlock the old hard disks
10917 * for which the new implicit diffs were created and will lock these new diffs for
10918 * writing.
10919 *
10920 * @param aOnline Whether the VM was online prior to this operation.
10921 *
10922 * @note Locks this object for writing!
10923 */
10924void Machine::commitMedia(bool aOnline /*= false*/)
10925{
10926 AutoCaller autoCaller(this);
10927 AssertComRCReturnVoid(autoCaller.rc());
10928
10929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10930
10931 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10932
10933 HRESULT rc = S_OK;
10934
10935 /* no attach/detach operations -- nothing to do */
10936 if (!mMediaData.isBackedUp())
10937 return;
10938
10939 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10940 bool fMediaNeedsLocking = false;
10941
10942 /* enumerate new attachments */
10943 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10944 it != mMediaData->mAttachments.end();
10945 ++it)
10946 {
10947 MediumAttachment *pAttach = *it;
10948
10949 pAttach->commit();
10950
10951 Medium* pMedium = pAttach->getMedium();
10952 bool fImplicit = pAttach->isImplicit();
10953
10954 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10955 (pMedium) ? pMedium->getName().c_str() : "NULL",
10956 fImplicit));
10957
10958 /** @todo convert all this Machine-based voodoo to MediumAttachment
10959 * based commit logic. */
10960 if (fImplicit)
10961 {
10962 /* convert implicit attachment to normal */
10963 pAttach->setImplicit(false);
10964
10965 if ( aOnline
10966 && pMedium
10967 && pAttach->getType() == DeviceType_HardDisk
10968 )
10969 {
10970 ComObjPtr<Medium> parent = pMedium->getParent();
10971 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10972
10973 /* update the appropriate lock list */
10974 MediumLockList *pMediumLockList;
10975 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10976 AssertComRC(rc);
10977 if (pMediumLockList)
10978 {
10979 /* unlock if there's a need to change the locking */
10980 if (!fMediaNeedsLocking)
10981 {
10982 rc = mData->mSession.mLockedMedia.Unlock();
10983 AssertComRC(rc);
10984 fMediaNeedsLocking = true;
10985 }
10986 rc = pMediumLockList->Update(parent, false);
10987 AssertComRC(rc);
10988 rc = pMediumLockList->Append(pMedium, true);
10989 AssertComRC(rc);
10990 }
10991 }
10992
10993 continue;
10994 }
10995
10996 if (pMedium)
10997 {
10998 /* was this medium attached before? */
10999 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11000 oldIt != oldAtts.end();
11001 ++oldIt)
11002 {
11003 MediumAttachment *pOldAttach = *oldIt;
11004 if (pOldAttach->getMedium() == pMedium)
11005 {
11006 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11007
11008 /* yes: remove from old to avoid de-association */
11009 oldAtts.erase(oldIt);
11010 break;
11011 }
11012 }
11013 }
11014 }
11015
11016 /* enumerate remaining old attachments and de-associate from the
11017 * current machine state */
11018 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11019 it != oldAtts.end();
11020 ++it)
11021 {
11022 MediumAttachment *pAttach = *it;
11023 Medium* pMedium = pAttach->getMedium();
11024
11025 /* Detach only hard disks, since DVD/floppy media is detached
11026 * instantly in MountMedium. */
11027 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11028 {
11029 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11030
11031 /* now de-associate from the current machine state */
11032 rc = pMedium->removeBackReference(mData->mUuid);
11033 AssertComRC(rc);
11034
11035 if (aOnline)
11036 {
11037 /* unlock since medium is not used anymore */
11038 MediumLockList *pMediumLockList;
11039 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11040 AssertComRC(rc);
11041 if (pMediumLockList)
11042 {
11043 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11044 AssertComRC(rc);
11045 }
11046 }
11047 }
11048 }
11049
11050 /* take media locks again so that the locking state is consistent */
11051 if (fMediaNeedsLocking)
11052 {
11053 Assert(aOnline);
11054 rc = mData->mSession.mLockedMedia.Lock();
11055 AssertComRC(rc);
11056 }
11057
11058 /* commit the hard disk changes */
11059 mMediaData.commit();
11060
11061 if (isSessionMachine())
11062 {
11063 /*
11064 * Update the parent machine to point to the new owner.
11065 * This is necessary because the stored parent will point to the
11066 * session machine otherwise and cause crashes or errors later
11067 * when the session machine gets invalid.
11068 */
11069 /** @todo Change the MediumAttachment class to behave like any other
11070 * class in this regard by creating peer MediumAttachment
11071 * objects for session machines and share the data with the peer
11072 * machine.
11073 */
11074 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11075 it != mMediaData->mAttachments.end();
11076 ++it)
11077 {
11078 (*it)->updateParentMachine(mPeer);
11079 }
11080
11081 /* attach new data to the primary machine and reshare it */
11082 mPeer->mMediaData.attach(mMediaData);
11083 }
11084
11085 return;
11086}
11087
11088/**
11089 * Perform deferred deletion of implicitly created diffs.
11090 *
11091 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11092 * backed up).
11093 *
11094 * @note Locks this object for writing!
11095 */
11096void Machine::rollbackMedia()
11097{
11098 AutoCaller autoCaller(this);
11099 AssertComRCReturnVoid(autoCaller.rc());
11100
11101 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11102 LogFlowThisFunc(("Entering rollbackMedia\n"));
11103
11104 HRESULT rc = S_OK;
11105
11106 /* no attach/detach operations -- nothing to do */
11107 if (!mMediaData.isBackedUp())
11108 return;
11109
11110 /* enumerate new attachments */
11111 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11112 it != mMediaData->mAttachments.end();
11113 ++it)
11114 {
11115 MediumAttachment *pAttach = *it;
11116 /* Fix up the backrefs for DVD/floppy media. */
11117 if (pAttach->getType() != DeviceType_HardDisk)
11118 {
11119 Medium* pMedium = pAttach->getMedium();
11120 if (pMedium)
11121 {
11122 rc = pMedium->removeBackReference(mData->mUuid);
11123 AssertComRC(rc);
11124 }
11125 }
11126
11127 (*it)->rollback();
11128
11129 pAttach = *it;
11130 /* Fix up the backrefs for DVD/floppy media. */
11131 if (pAttach->getType() != DeviceType_HardDisk)
11132 {
11133 Medium* pMedium = pAttach->getMedium();
11134 if (pMedium)
11135 {
11136 rc = pMedium->addBackReference(mData->mUuid);
11137 AssertComRC(rc);
11138 }
11139 }
11140 }
11141
11142 /** @todo convert all this Machine-based voodoo to MediumAttachment
11143 * based rollback logic. */
11144 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11145
11146 return;
11147}
11148
11149/**
11150 * Returns true if the settings file is located in the directory named exactly
11151 * as the machine; this means, among other things, that the machine directory
11152 * should be auto-renamed.
11153 *
11154 * @param aSettingsDir if not NULL, the full machine settings file directory
11155 * name will be assigned there.
11156 *
11157 * @note Doesn't lock anything.
11158 * @note Not thread safe (must be called from this object's lock).
11159 */
11160bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11161{
11162 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11163 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11164 if (aSettingsDir)
11165 *aSettingsDir = strMachineDirName;
11166 strMachineDirName.stripPath(); // vmname
11167 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11168 strConfigFileOnly.stripPath() // vmname.vbox
11169 .stripExt(); // vmname
11170 /** @todo hack, make somehow use of ComposeMachineFilename */
11171 if (mUserData->s.fDirectoryIncludesUUID)
11172 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11173
11174 AssertReturn(!strMachineDirName.isEmpty(), false);
11175 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11176
11177 return strMachineDirName == strConfigFileOnly;
11178}
11179
11180/**
11181 * Discards all changes to machine settings.
11182 *
11183 * @param aNotify Whether to notify the direct session about changes or not.
11184 *
11185 * @note Locks objects for writing!
11186 */
11187void Machine::rollback(bool aNotify)
11188{
11189 AutoCaller autoCaller(this);
11190 AssertComRCReturn(autoCaller.rc(), (void)0);
11191
11192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11193
11194 if (!mStorageControllers.isNull())
11195 {
11196 if (mStorageControllers.isBackedUp())
11197 {
11198 /* unitialize all new devices (absent in the backed up list). */
11199 StorageControllerList::const_iterator it = mStorageControllers->begin();
11200 StorageControllerList *backedList = mStorageControllers.backedUpData();
11201 while (it != mStorageControllers->end())
11202 {
11203 if ( std::find(backedList->begin(), backedList->end(), *it)
11204 == backedList->end()
11205 )
11206 {
11207 (*it)->uninit();
11208 }
11209 ++it;
11210 }
11211
11212 /* restore the list */
11213 mStorageControllers.rollback();
11214 }
11215
11216 /* rollback any changes to devices after restoring the list */
11217 if (mData->flModifications & IsModified_Storage)
11218 {
11219 StorageControllerList::const_iterator it = mStorageControllers->begin();
11220 while (it != mStorageControllers->end())
11221 {
11222 (*it)->rollback();
11223 ++it;
11224 }
11225 }
11226 }
11227
11228 mUserData.rollback();
11229
11230 mHWData.rollback();
11231
11232 if (mData->flModifications & IsModified_Storage)
11233 rollbackMedia();
11234
11235 if (mBIOSSettings)
11236 mBIOSSettings->rollback();
11237
11238 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11239 mVRDEServer->rollback();
11240
11241 if (mAudioAdapter)
11242 mAudioAdapter->rollback();
11243
11244 if (mUSBController && (mData->flModifications & IsModified_USB))
11245 mUSBController->rollback();
11246
11247 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11248 mBandwidthControl->rollback();
11249
11250 if (!mHWData.isNull())
11251 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11252 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11253 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11254 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11255
11256 if (mData->flModifications & IsModified_NetworkAdapters)
11257 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11258 if ( mNetworkAdapters[slot]
11259 && mNetworkAdapters[slot]->isModified())
11260 {
11261 mNetworkAdapters[slot]->rollback();
11262 networkAdapters[slot] = mNetworkAdapters[slot];
11263 }
11264
11265 if (mData->flModifications & IsModified_SerialPorts)
11266 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11267 if ( mSerialPorts[slot]
11268 && mSerialPorts[slot]->isModified())
11269 {
11270 mSerialPorts[slot]->rollback();
11271 serialPorts[slot] = mSerialPorts[slot];
11272 }
11273
11274 if (mData->flModifications & IsModified_ParallelPorts)
11275 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11276 if ( mParallelPorts[slot]
11277 && mParallelPorts[slot]->isModified())
11278 {
11279 mParallelPorts[slot]->rollback();
11280 parallelPorts[slot] = mParallelPorts[slot];
11281 }
11282
11283 if (aNotify)
11284 {
11285 /* inform the direct session about changes */
11286
11287 ComObjPtr<Machine> that = this;
11288 uint32_t flModifications = mData->flModifications;
11289 alock.release();
11290
11291 if (flModifications & IsModified_SharedFolders)
11292 that->onSharedFolderChange();
11293
11294 if (flModifications & IsModified_VRDEServer)
11295 that->onVRDEServerChange(/* aRestart */ TRUE);
11296 if (flModifications & IsModified_USB)
11297 that->onUSBControllerChange();
11298
11299 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11300 if (networkAdapters[slot])
11301 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11302 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11303 if (serialPorts[slot])
11304 that->onSerialPortChange(serialPorts[slot]);
11305 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11306 if (parallelPorts[slot])
11307 that->onParallelPortChange(parallelPorts[slot]);
11308
11309 if (flModifications & IsModified_Storage)
11310 that->onStorageControllerChange();
11311
11312#if 0
11313 if (flModifications & IsModified_BandwidthControl)
11314 that->onBandwidthControlChange();
11315#endif
11316 }
11317}
11318
11319/**
11320 * Commits all the changes to machine settings.
11321 *
11322 * Note that this operation is supposed to never fail.
11323 *
11324 * @note Locks this object and children for writing.
11325 */
11326void Machine::commit()
11327{
11328 AutoCaller autoCaller(this);
11329 AssertComRCReturnVoid(autoCaller.rc());
11330
11331 AutoCaller peerCaller(mPeer);
11332 AssertComRCReturnVoid(peerCaller.rc());
11333
11334 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11335
11336 /*
11337 * use safe commit to ensure Snapshot machines (that share mUserData)
11338 * will still refer to a valid memory location
11339 */
11340 mUserData.commitCopy();
11341
11342 mHWData.commit();
11343
11344 if (mMediaData.isBackedUp())
11345 commitMedia();
11346
11347 mBIOSSettings->commit();
11348 mVRDEServer->commit();
11349 mAudioAdapter->commit();
11350 mUSBController->commit();
11351 mBandwidthControl->commit();
11352
11353 /* Since mNetworkAdapters is a list which might have been changed (resized)
11354 * without using the Backupable<> template we need to handle the copying
11355 * of the list entries manually, including the creation of peers for the
11356 * new objects. */
11357 bool commitNetworkAdapters = false;
11358 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11359 if (mPeer)
11360 {
11361 /* commit everything, even the ones which will go away */
11362 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11363 mNetworkAdapters[slot]->commit();
11364 /* copy over the new entries, creating a peer and uninit the original */
11365 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11366 for (size_t slot = 0; slot < newSize; slot++)
11367 {
11368 /* look if this adapter has a peer device */
11369 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11370 if (!peer)
11371 {
11372 /* no peer means the adapter is a newly created one;
11373 * create a peer owning data this data share it with */
11374 peer.createObject();
11375 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11376 }
11377 mPeer->mNetworkAdapters[slot] = peer;
11378 }
11379 /* uninit any no longer needed network adapters */
11380 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11381 mNetworkAdapters[slot]->uninit();
11382 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11383 {
11384 if (mPeer->mNetworkAdapters[slot])
11385 mPeer->mNetworkAdapters[slot]->uninit();
11386 }
11387 /* Keep the original network adapter count until this point, so that
11388 * discarding a chipset type change will not lose settings. */
11389 mNetworkAdapters.resize(newSize);
11390 mPeer->mNetworkAdapters.resize(newSize);
11391 }
11392 else
11393 {
11394 /* we have no peer (our parent is the newly created machine);
11395 * just commit changes to the network adapters */
11396 commitNetworkAdapters = true;
11397 }
11398 if (commitNetworkAdapters)
11399 {
11400 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11401 mNetworkAdapters[slot]->commit();
11402 }
11403
11404 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11405 mSerialPorts[slot]->commit();
11406 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11407 mParallelPorts[slot]->commit();
11408
11409 bool commitStorageControllers = false;
11410
11411 if (mStorageControllers.isBackedUp())
11412 {
11413 mStorageControllers.commit();
11414
11415 if (mPeer)
11416 {
11417 /* Commit all changes to new controllers (this will reshare data with
11418 * peers for those who have peers) */
11419 StorageControllerList *newList = new StorageControllerList();
11420 StorageControllerList::const_iterator it = mStorageControllers->begin();
11421 while (it != mStorageControllers->end())
11422 {
11423 (*it)->commit();
11424
11425 /* look if this controller has a peer device */
11426 ComObjPtr<StorageController> peer = (*it)->getPeer();
11427 if (!peer)
11428 {
11429 /* no peer means the device is a newly created one;
11430 * create a peer owning data this device share it with */
11431 peer.createObject();
11432 peer->init(mPeer, *it, true /* aReshare */);
11433 }
11434 else
11435 {
11436 /* remove peer from the old list */
11437 mPeer->mStorageControllers->remove(peer);
11438 }
11439 /* and add it to the new list */
11440 newList->push_back(peer);
11441
11442 ++it;
11443 }
11444
11445 /* uninit old peer's controllers that are left */
11446 it = mPeer->mStorageControllers->begin();
11447 while (it != mPeer->mStorageControllers->end())
11448 {
11449 (*it)->uninit();
11450 ++it;
11451 }
11452
11453 /* attach new list of controllers to our peer */
11454 mPeer->mStorageControllers.attach(newList);
11455 }
11456 else
11457 {
11458 /* we have no peer (our parent is the newly created machine);
11459 * just commit changes to devices */
11460 commitStorageControllers = true;
11461 }
11462 }
11463 else
11464 {
11465 /* the list of controllers itself is not changed,
11466 * just commit changes to controllers themselves */
11467 commitStorageControllers = true;
11468 }
11469
11470 if (commitStorageControllers)
11471 {
11472 StorageControllerList::const_iterator it = mStorageControllers->begin();
11473 while (it != mStorageControllers->end())
11474 {
11475 (*it)->commit();
11476 ++it;
11477 }
11478 }
11479
11480 if (isSessionMachine())
11481 {
11482 /* attach new data to the primary machine and reshare it */
11483 mPeer->mUserData.attach(mUserData);
11484 mPeer->mHWData.attach(mHWData);
11485 /* mMediaData is reshared by fixupMedia */
11486 // mPeer->mMediaData.attach(mMediaData);
11487 Assert(mPeer->mMediaData.data() == mMediaData.data());
11488 }
11489}
11490
11491/**
11492 * Copies all the hardware data from the given machine.
11493 *
11494 * Currently, only called when the VM is being restored from a snapshot. In
11495 * particular, this implies that the VM is not running during this method's
11496 * call.
11497 *
11498 * @note This method must be called from under this object's lock.
11499 *
11500 * @note This method doesn't call #commit(), so all data remains backed up and
11501 * unsaved.
11502 */
11503void Machine::copyFrom(Machine *aThat)
11504{
11505 AssertReturnVoid(!isSnapshotMachine());
11506 AssertReturnVoid(aThat->isSnapshotMachine());
11507
11508 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11509
11510 mHWData.assignCopy(aThat->mHWData);
11511
11512 // create copies of all shared folders (mHWData after attaching a copy
11513 // contains just references to original objects)
11514 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11515 it != mHWData->mSharedFolders.end();
11516 ++it)
11517 {
11518 ComObjPtr<SharedFolder> folder;
11519 folder.createObject();
11520 HRESULT rc = folder->initCopy(getMachine(), *it);
11521 AssertComRC(rc);
11522 *it = folder;
11523 }
11524
11525 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11526 mVRDEServer->copyFrom(aThat->mVRDEServer);
11527 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11528 mUSBController->copyFrom(aThat->mUSBController);
11529 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11530
11531 /* create private copies of all controllers */
11532 mStorageControllers.backup();
11533 mStorageControllers->clear();
11534 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11535 it != aThat->mStorageControllers->end();
11536 ++it)
11537 {
11538 ComObjPtr<StorageController> ctrl;
11539 ctrl.createObject();
11540 ctrl->initCopy(this, *it);
11541 mStorageControllers->push_back(ctrl);
11542 }
11543
11544 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11545 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11546 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11547 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11548 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11549 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11550 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11551}
11552
11553/**
11554 * Returns whether the given storage controller is hotplug capable.
11555 *
11556 * @returns true if the controller supports hotplugging
11557 * false otherwise.
11558 * @param enmCtrlType The controller type to check for.
11559 */
11560bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11561{
11562 switch (enmCtrlType)
11563 {
11564 case StorageControllerType_IntelAhci:
11565 return true;
11566 case StorageControllerType_LsiLogic:
11567 case StorageControllerType_LsiLogicSas:
11568 case StorageControllerType_BusLogic:
11569 case StorageControllerType_PIIX3:
11570 case StorageControllerType_PIIX4:
11571 case StorageControllerType_ICH6:
11572 case StorageControllerType_I82078:
11573 default:
11574 return false;
11575 }
11576}
11577
11578#ifdef VBOX_WITH_RESOURCE_USAGE_API
11579
11580void Machine::getDiskList(MediaList &list)
11581{
11582 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11583 it != mMediaData->mAttachments.end();
11584 ++it)
11585 {
11586 MediumAttachment* pAttach = *it;
11587 /* just in case */
11588 AssertStmt(pAttach, continue);
11589
11590 AutoCaller localAutoCallerA(pAttach);
11591 if (FAILED(localAutoCallerA.rc())) continue;
11592
11593 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11594
11595 if (pAttach->getType() == DeviceType_HardDisk)
11596 list.push_back(pAttach->getMedium());
11597 }
11598}
11599
11600void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11601{
11602 AssertReturnVoid(isWriteLockOnCurrentThread());
11603 AssertPtrReturnVoid(aCollector);
11604
11605 pm::CollectorHAL *hal = aCollector->getHAL();
11606 /* Create sub metrics */
11607 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11608 "Percentage of processor time spent in user mode by the VM process.");
11609 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11610 "Percentage of processor time spent in kernel mode by the VM process.");
11611 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11612 "Size of resident portion of VM process in memory.");
11613 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11614 "Actual size of all VM disks combined.");
11615 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11616 "Network receive rate.");
11617 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11618 "Network transmit rate.");
11619 /* Create and register base metrics */
11620 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11621 cpuLoadUser, cpuLoadKernel);
11622 aCollector->registerBaseMetric(cpuLoad);
11623 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11624 ramUsageUsed);
11625 aCollector->registerBaseMetric(ramUsage);
11626 MediaList disks;
11627 getDiskList(disks);
11628 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11629 diskUsageUsed);
11630 aCollector->registerBaseMetric(diskUsage);
11631
11632 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11633 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11634 new pm::AggregateAvg()));
11635 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11636 new pm::AggregateMin()));
11637 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11638 new pm::AggregateMax()));
11639 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11640 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11641 new pm::AggregateAvg()));
11642 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11643 new pm::AggregateMin()));
11644 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11645 new pm::AggregateMax()));
11646
11647 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11648 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11649 new pm::AggregateAvg()));
11650 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11651 new pm::AggregateMin()));
11652 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11653 new pm::AggregateMax()));
11654
11655 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11656 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11657 new pm::AggregateAvg()));
11658 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11659 new pm::AggregateMin()));
11660 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11661 new pm::AggregateMax()));
11662
11663
11664 /* Guest metrics collector */
11665 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11666 aCollector->registerGuest(mCollectorGuest);
11667 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11668 this, __PRETTY_FUNCTION__, mCollectorGuest));
11669
11670 /* Create sub metrics */
11671 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11672 "Percentage of processor time spent in user mode as seen by the guest.");
11673 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11674 "Percentage of processor time spent in kernel mode as seen by the guest.");
11675 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11676 "Percentage of processor time spent idling as seen by the guest.");
11677
11678 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11679 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11680 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11681 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11682 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11683 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11684
11685 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11686
11687 /* Create and register base metrics */
11688 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11689 machineNetRx, machineNetTx);
11690 aCollector->registerBaseMetric(machineNetRate);
11691
11692 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11693 guestLoadUser, guestLoadKernel, guestLoadIdle);
11694 aCollector->registerBaseMetric(guestCpuLoad);
11695
11696 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11697 guestMemTotal, guestMemFree,
11698 guestMemBalloon, guestMemShared,
11699 guestMemCache, guestPagedTotal);
11700 aCollector->registerBaseMetric(guestCpuMem);
11701
11702 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11703 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11704 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11705 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11706
11707 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11708 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11709 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11710 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11711
11712 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11713 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11714 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11715 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11716
11717 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11718 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11719 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11720 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11721
11722 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11723 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11724 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11725 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11726
11727 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11728 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11729 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11730 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11731
11732 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11733 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11734 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11735 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11736
11737 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11738 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11739 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11740 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11741
11742 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11743 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11744 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11745 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11746
11747 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11748 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11749 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11750 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11751
11752 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11753 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11754 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11755 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11756}
11757
11758void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11759{
11760 AssertReturnVoid(isWriteLockOnCurrentThread());
11761
11762 if (aCollector)
11763 {
11764 aCollector->unregisterMetricsFor(aMachine);
11765 aCollector->unregisterBaseMetricsFor(aMachine);
11766 }
11767}
11768
11769#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11770
11771
11772////////////////////////////////////////////////////////////////////////////////
11773
11774DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11775
11776HRESULT SessionMachine::FinalConstruct()
11777{
11778 LogFlowThisFunc(("\n"));
11779
11780#if defined(RT_OS_WINDOWS)
11781 mIPCSem = NULL;
11782#elif defined(RT_OS_OS2)
11783 mIPCSem = NULLHANDLE;
11784#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11785 mIPCSem = -1;
11786#else
11787# error "Port me!"
11788#endif
11789
11790 return BaseFinalConstruct();
11791}
11792
11793void SessionMachine::FinalRelease()
11794{
11795 LogFlowThisFunc(("\n"));
11796
11797 uninit(Uninit::Unexpected);
11798
11799 BaseFinalRelease();
11800}
11801
11802/**
11803 * @note Must be called only by Machine::openSession() from its own write lock.
11804 */
11805HRESULT SessionMachine::init(Machine *aMachine)
11806{
11807 LogFlowThisFuncEnter();
11808 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11809
11810 AssertReturn(aMachine, E_INVALIDARG);
11811
11812 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11813
11814 /* Enclose the state transition NotReady->InInit->Ready */
11815 AutoInitSpan autoInitSpan(this);
11816 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11817
11818 /* create the interprocess semaphore */
11819#if defined(RT_OS_WINDOWS)
11820 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11821 for (size_t i = 0; i < mIPCSemName.length(); i++)
11822 if (mIPCSemName.raw()[i] == '\\')
11823 mIPCSemName.raw()[i] = '/';
11824 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11825 ComAssertMsgRet(mIPCSem,
11826 ("Cannot create IPC mutex '%ls', err=%d",
11827 mIPCSemName.raw(), ::GetLastError()),
11828 E_FAIL);
11829#elif defined(RT_OS_OS2)
11830 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11831 aMachine->mData->mUuid.raw());
11832 mIPCSemName = ipcSem;
11833 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11834 ComAssertMsgRet(arc == NO_ERROR,
11835 ("Cannot create IPC mutex '%s', arc=%ld",
11836 ipcSem.c_str(), arc),
11837 E_FAIL);
11838#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11839# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11840# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11841 /** @todo Check that this still works correctly. */
11842 AssertCompileSize(key_t, 8);
11843# else
11844 AssertCompileSize(key_t, 4);
11845# endif
11846 key_t key;
11847 mIPCSem = -1;
11848 mIPCKey = "0";
11849 for (uint32_t i = 0; i < 1 << 24; i++)
11850 {
11851 key = ((uint32_t)'V' << 24) | i;
11852 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11853 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11854 {
11855 mIPCSem = sem;
11856 if (sem >= 0)
11857 mIPCKey = BstrFmt("%u", key);
11858 break;
11859 }
11860 }
11861# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11862 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11863 char *pszSemName = NULL;
11864 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11865 key_t key = ::ftok(pszSemName, 'V');
11866 RTStrFree(pszSemName);
11867
11868 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11869# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11870
11871 int errnoSave = errno;
11872 if (mIPCSem < 0 && errnoSave == ENOSYS)
11873 {
11874 setError(E_FAIL,
11875 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11876 "support for SysV IPC. Check the host kernel configuration for "
11877 "CONFIG_SYSVIPC=y"));
11878 return E_FAIL;
11879 }
11880 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11881 * the IPC semaphores */
11882 if (mIPCSem < 0 && errnoSave == ENOSPC)
11883 {
11884#ifdef RT_OS_LINUX
11885 setError(E_FAIL,
11886 tr("Cannot create IPC semaphore because the system limit for the "
11887 "maximum number of semaphore sets (SEMMNI), or the system wide "
11888 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11889 "current set of SysV IPC semaphores can be determined from "
11890 "the file /proc/sysvipc/sem"));
11891#else
11892 setError(E_FAIL,
11893 tr("Cannot create IPC semaphore because the system-imposed limit "
11894 "on the maximum number of allowed semaphores or semaphore "
11895 "identifiers system-wide would be exceeded"));
11896#endif
11897 return E_FAIL;
11898 }
11899 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11900 E_FAIL);
11901 /* set the initial value to 1 */
11902 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11903 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11904 E_FAIL);
11905#else
11906# error "Port me!"
11907#endif
11908
11909 /* memorize the peer Machine */
11910 unconst(mPeer) = aMachine;
11911 /* share the parent pointer */
11912 unconst(mParent) = aMachine->mParent;
11913
11914 /* take the pointers to data to share */
11915 mData.share(aMachine->mData);
11916 mSSData.share(aMachine->mSSData);
11917
11918 mUserData.share(aMachine->mUserData);
11919 mHWData.share(aMachine->mHWData);
11920 mMediaData.share(aMachine->mMediaData);
11921
11922 mStorageControllers.allocate();
11923 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11924 it != aMachine->mStorageControllers->end();
11925 ++it)
11926 {
11927 ComObjPtr<StorageController> ctl;
11928 ctl.createObject();
11929 ctl->init(this, *it);
11930 mStorageControllers->push_back(ctl);
11931 }
11932
11933 unconst(mBIOSSettings).createObject();
11934 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11935 /* create another VRDEServer object that will be mutable */
11936 unconst(mVRDEServer).createObject();
11937 mVRDEServer->init(this, aMachine->mVRDEServer);
11938 /* create another audio adapter object that will be mutable */
11939 unconst(mAudioAdapter).createObject();
11940 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11941 /* create a list of serial ports that will be mutable */
11942 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11943 {
11944 unconst(mSerialPorts[slot]).createObject();
11945 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11946 }
11947 /* create a list of parallel ports that will be mutable */
11948 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11949 {
11950 unconst(mParallelPorts[slot]).createObject();
11951 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11952 }
11953 /* create another USB controller object that will be mutable */
11954 unconst(mUSBController).createObject();
11955 mUSBController->init(this, aMachine->mUSBController);
11956
11957 /* create a list of network adapters that will be mutable */
11958 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11959 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11960 {
11961 unconst(mNetworkAdapters[slot]).createObject();
11962 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11963 }
11964
11965 /* create another bandwidth control object that will be mutable */
11966 unconst(mBandwidthControl).createObject();
11967 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11968
11969 /* default is to delete saved state on Saved -> PoweredOff transition */
11970 mRemoveSavedState = true;
11971
11972 /* Confirm a successful initialization when it's the case */
11973 autoInitSpan.setSucceeded();
11974
11975 LogFlowThisFuncLeave();
11976 return S_OK;
11977}
11978
11979/**
11980 * Uninitializes this session object. If the reason is other than
11981 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11982 *
11983 * @param aReason uninitialization reason
11984 *
11985 * @note Locks mParent + this object for writing.
11986 */
11987void SessionMachine::uninit(Uninit::Reason aReason)
11988{
11989 LogFlowThisFuncEnter();
11990 LogFlowThisFunc(("reason=%d\n", aReason));
11991
11992 /*
11993 * Strongly reference ourselves to prevent this object deletion after
11994 * mData->mSession.mMachine.setNull() below (which can release the last
11995 * reference and call the destructor). Important: this must be done before
11996 * accessing any members (and before AutoUninitSpan that does it as well).
11997 * This self reference will be released as the very last step on return.
11998 */
11999 ComObjPtr<SessionMachine> selfRef = this;
12000
12001 /* Enclose the state transition Ready->InUninit->NotReady */
12002 AutoUninitSpan autoUninitSpan(this);
12003 if (autoUninitSpan.uninitDone())
12004 {
12005 LogFlowThisFunc(("Already uninitialized\n"));
12006 LogFlowThisFuncLeave();
12007 return;
12008 }
12009
12010 if (autoUninitSpan.initFailed())
12011 {
12012 /* We've been called by init() because it's failed. It's not really
12013 * necessary (nor it's safe) to perform the regular uninit sequence
12014 * below, the following is enough.
12015 */
12016 LogFlowThisFunc(("Initialization failed.\n"));
12017#if defined(RT_OS_WINDOWS)
12018 if (mIPCSem)
12019 ::CloseHandle(mIPCSem);
12020 mIPCSem = NULL;
12021#elif defined(RT_OS_OS2)
12022 if (mIPCSem != NULLHANDLE)
12023 ::DosCloseMutexSem(mIPCSem);
12024 mIPCSem = NULLHANDLE;
12025#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12026 if (mIPCSem >= 0)
12027 ::semctl(mIPCSem, 0, IPC_RMID);
12028 mIPCSem = -1;
12029# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12030 mIPCKey = "0";
12031# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12032#else
12033# error "Port me!"
12034#endif
12035 uninitDataAndChildObjects();
12036 mData.free();
12037 unconst(mParent) = NULL;
12038 unconst(mPeer) = NULL;
12039 LogFlowThisFuncLeave();
12040 return;
12041 }
12042
12043 MachineState_T lastState;
12044 {
12045 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12046 lastState = mData->mMachineState;
12047 }
12048 NOREF(lastState);
12049
12050#ifdef VBOX_WITH_USB
12051 // release all captured USB devices, but do this before requesting the locks below
12052 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12053 {
12054 /* Console::captureUSBDevices() is called in the VM process only after
12055 * setting the machine state to Starting or Restoring.
12056 * Console::detachAllUSBDevices() will be called upon successful
12057 * termination. So, we need to release USB devices only if there was
12058 * an abnormal termination of a running VM.
12059 *
12060 * This is identical to SessionMachine::DetachAllUSBDevices except
12061 * for the aAbnormal argument. */
12062 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12063 AssertComRC(rc);
12064 NOREF(rc);
12065
12066 USBProxyService *service = mParent->host()->usbProxyService();
12067 if (service)
12068 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12069 }
12070#endif /* VBOX_WITH_USB */
12071
12072 // we need to lock this object in uninit() because the lock is shared
12073 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12074 // and others need mParent lock, and USB needs host lock.
12075 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12076
12077#if 0
12078 // Trigger async cleanup tasks, avoid doing things here which are not
12079 // vital to be done immediately and maybe need more locks. This calls
12080 // Machine::unregisterMetrics().
12081 mParent->onMachineUninit(mPeer);
12082#else
12083 /*
12084 * It is safe to call Machine::unregisterMetrics() here because
12085 * PerformanceCollector::samplerCallback no longer accesses guest methods
12086 * holding the lock.
12087 */
12088 unregisterMetrics(mParent->performanceCollector(), mPeer);
12089#endif
12090 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12091 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12092 this, __PRETTY_FUNCTION__, mCollectorGuest));
12093 if (mCollectorGuest)
12094 {
12095 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12096 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12097 mCollectorGuest = NULL;
12098 }
12099
12100 if (aReason == Uninit::Abnormal)
12101 {
12102 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12103 Global::IsOnlineOrTransient(lastState)));
12104
12105 /* reset the state to Aborted */
12106 if (mData->mMachineState != MachineState_Aborted)
12107 setMachineState(MachineState_Aborted);
12108 }
12109
12110 // any machine settings modified?
12111 if (mData->flModifications)
12112 {
12113 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12114 rollback(false /* aNotify */);
12115 }
12116
12117 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12118 || !mConsoleTaskData.mSnapshot);
12119 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12120 {
12121 LogWarningThisFunc(("canceling failed save state request!\n"));
12122 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12123 }
12124 else if (!mConsoleTaskData.mSnapshot.isNull())
12125 {
12126 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12127
12128 /* delete all differencing hard disks created (this will also attach
12129 * their parents back by rolling back mMediaData) */
12130 rollbackMedia();
12131
12132 // delete the saved state file (it might have been already created)
12133 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12134 // think it's still in use
12135 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12136 mConsoleTaskData.mSnapshot->uninit();
12137 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12138 }
12139
12140 if (!mData->mSession.mType.isEmpty())
12141 {
12142 /* mType is not null when this machine's process has been started by
12143 * Machine::LaunchVMProcess(), therefore it is our child. We
12144 * need to queue the PID to reap the process (and avoid zombies on
12145 * Linux). */
12146 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12147 mParent->addProcessToReap(mData->mSession.mPID);
12148 }
12149
12150 mData->mSession.mPID = NIL_RTPROCESS;
12151
12152 if (aReason == Uninit::Unexpected)
12153 {
12154 /* Uninitialization didn't come from #checkForDeath(), so tell the
12155 * client watcher thread to update the set of machines that have open
12156 * sessions. */
12157 mParent->updateClientWatcher();
12158 }
12159
12160 /* uninitialize all remote controls */
12161 if (mData->mSession.mRemoteControls.size())
12162 {
12163 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12164 mData->mSession.mRemoteControls.size()));
12165
12166 Data::Session::RemoteControlList::iterator it =
12167 mData->mSession.mRemoteControls.begin();
12168 while (it != mData->mSession.mRemoteControls.end())
12169 {
12170 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12171 HRESULT rc = (*it)->Uninitialize();
12172 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12173 if (FAILED(rc))
12174 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12175 ++it;
12176 }
12177 mData->mSession.mRemoteControls.clear();
12178 }
12179
12180 /*
12181 * An expected uninitialization can come only from #checkForDeath().
12182 * Otherwise it means that something's gone really wrong (for example,
12183 * the Session implementation has released the VirtualBox reference
12184 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12185 * etc). However, it's also possible, that the client releases the IPC
12186 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12187 * but the VirtualBox release event comes first to the server process.
12188 * This case is practically possible, so we should not assert on an
12189 * unexpected uninit, just log a warning.
12190 */
12191
12192 if ((aReason == Uninit::Unexpected))
12193 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12194
12195 if (aReason != Uninit::Normal)
12196 {
12197 mData->mSession.mDirectControl.setNull();
12198 }
12199 else
12200 {
12201 /* this must be null here (see #OnSessionEnd()) */
12202 Assert(mData->mSession.mDirectControl.isNull());
12203 Assert(mData->mSession.mState == SessionState_Unlocking);
12204 Assert(!mData->mSession.mProgress.isNull());
12205 }
12206 if (mData->mSession.mProgress)
12207 {
12208 if (aReason == Uninit::Normal)
12209 mData->mSession.mProgress->notifyComplete(S_OK);
12210 else
12211 mData->mSession.mProgress->notifyComplete(E_FAIL,
12212 COM_IIDOF(ISession),
12213 getComponentName(),
12214 tr("The VM session was aborted"));
12215 mData->mSession.mProgress.setNull();
12216 }
12217
12218 /* remove the association between the peer machine and this session machine */
12219 Assert( (SessionMachine*)mData->mSession.mMachine == this
12220 || aReason == Uninit::Unexpected);
12221
12222 /* reset the rest of session data */
12223 mData->mSession.mMachine.setNull();
12224 mData->mSession.mState = SessionState_Unlocked;
12225 mData->mSession.mType.setNull();
12226
12227 /* close the interprocess semaphore before leaving the exclusive lock */
12228#if defined(RT_OS_WINDOWS)
12229 if (mIPCSem)
12230 ::CloseHandle(mIPCSem);
12231 mIPCSem = NULL;
12232#elif defined(RT_OS_OS2)
12233 if (mIPCSem != NULLHANDLE)
12234 ::DosCloseMutexSem(mIPCSem);
12235 mIPCSem = NULLHANDLE;
12236#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12237 if (mIPCSem >= 0)
12238 ::semctl(mIPCSem, 0, IPC_RMID);
12239 mIPCSem = -1;
12240# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12241 mIPCKey = "0";
12242# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12243#else
12244# error "Port me!"
12245#endif
12246
12247 /* fire an event */
12248 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12249
12250 uninitDataAndChildObjects();
12251
12252 /* free the essential data structure last */
12253 mData.free();
12254
12255 /* release the exclusive lock before setting the below two to NULL */
12256 multilock.release();
12257
12258 unconst(mParent) = NULL;
12259 unconst(mPeer) = NULL;
12260
12261 LogFlowThisFuncLeave();
12262}
12263
12264// util::Lockable interface
12265////////////////////////////////////////////////////////////////////////////////
12266
12267/**
12268 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12269 * with the primary Machine instance (mPeer).
12270 */
12271RWLockHandle *SessionMachine::lockHandle() const
12272{
12273 AssertReturn(mPeer != NULL, NULL);
12274 return mPeer->lockHandle();
12275}
12276
12277// IInternalMachineControl methods
12278////////////////////////////////////////////////////////////////////////////////
12279
12280/**
12281 * Passes collected guest statistics to performance collector object
12282 */
12283STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12284 ULONG aCpuKernel, ULONG aCpuIdle,
12285 ULONG aMemTotal, ULONG aMemFree,
12286 ULONG aMemBalloon, ULONG aMemShared,
12287 ULONG aMemCache, ULONG aPageTotal,
12288 ULONG aAllocVMM, ULONG aFreeVMM,
12289 ULONG aBalloonedVMM, ULONG aSharedVMM,
12290 ULONG aVmNetRx, ULONG aVmNetTx)
12291{
12292 if (mCollectorGuest)
12293 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12294 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12295 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12296 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12297
12298 return S_OK;
12299}
12300
12301/**
12302 * @note Locks this object for writing.
12303 */
12304STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12305{
12306 AutoCaller autoCaller(this);
12307 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12308
12309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12310
12311 mRemoveSavedState = aRemove;
12312
12313 return S_OK;
12314}
12315
12316/**
12317 * @note Locks the same as #setMachineState() does.
12318 */
12319STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12320{
12321 return setMachineState(aMachineState);
12322}
12323
12324/**
12325 * @note Locks this object for reading.
12326 */
12327STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12328{
12329 AutoCaller autoCaller(this);
12330 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12331
12332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12333
12334#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12335 mIPCSemName.cloneTo(aId);
12336 return S_OK;
12337#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12338# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12339 mIPCKey.cloneTo(aId);
12340# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12341 mData->m_strConfigFileFull.cloneTo(aId);
12342# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12343 return S_OK;
12344#else
12345# error "Port me!"
12346#endif
12347}
12348
12349/**
12350 * @note Locks this object for writing.
12351 */
12352STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12353{
12354 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12355 AutoCaller autoCaller(this);
12356 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12357
12358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12359
12360 if (mData->mSession.mState != SessionState_Locked)
12361 return VBOX_E_INVALID_OBJECT_STATE;
12362
12363 if (!mData->mSession.mProgress.isNull())
12364 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12365
12366 LogFlowThisFunc(("returns S_OK.\n"));
12367 return S_OK;
12368}
12369
12370/**
12371 * @note Locks this object for writing.
12372 */
12373STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12374{
12375 AutoCaller autoCaller(this);
12376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12377
12378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12379
12380 if (mData->mSession.mState != SessionState_Locked)
12381 return VBOX_E_INVALID_OBJECT_STATE;
12382
12383 /* Finalize the LaunchVMProcess progress object. */
12384 if (mData->mSession.mProgress)
12385 {
12386 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12387 mData->mSession.mProgress.setNull();
12388 }
12389
12390 if (SUCCEEDED((HRESULT)iResult))
12391 {
12392#ifdef VBOX_WITH_RESOURCE_USAGE_API
12393 /* The VM has been powered up successfully, so it makes sense
12394 * now to offer the performance metrics for a running machine
12395 * object. Doing it earlier wouldn't be safe. */
12396 registerMetrics(mParent->performanceCollector(), mPeer,
12397 mData->mSession.mPID);
12398#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12399 }
12400
12401 return S_OK;
12402}
12403
12404/**
12405 * @note Locks this object for writing.
12406 */
12407STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12408{
12409 LogFlowThisFuncEnter();
12410
12411 CheckComArgOutPointerValid(aProgress);
12412
12413 AutoCaller autoCaller(this);
12414 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12415
12416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12417
12418 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12419 E_FAIL);
12420
12421 /* create a progress object to track operation completion */
12422 ComObjPtr<Progress> pProgress;
12423 pProgress.createObject();
12424 pProgress->init(getVirtualBox(),
12425 static_cast<IMachine *>(this) /* aInitiator */,
12426 Bstr(tr("Stopping the virtual machine")).raw(),
12427 FALSE /* aCancelable */);
12428
12429 /* fill in the console task data */
12430 mConsoleTaskData.mLastState = mData->mMachineState;
12431 mConsoleTaskData.mProgress = pProgress;
12432
12433 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12434 setMachineState(MachineState_Stopping);
12435
12436 pProgress.queryInterfaceTo(aProgress);
12437
12438 return S_OK;
12439}
12440
12441/**
12442 * @note Locks this object for writing.
12443 */
12444STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12445{
12446 LogFlowThisFuncEnter();
12447
12448 AutoCaller autoCaller(this);
12449 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12450
12451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12452
12453 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12454 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12455 && mConsoleTaskData.mLastState != MachineState_Null,
12456 E_FAIL);
12457
12458 /*
12459 * On failure, set the state to the state we had when BeginPoweringDown()
12460 * was called (this is expected by Console::PowerDown() and the associated
12461 * task). On success the VM process already changed the state to
12462 * MachineState_PoweredOff, so no need to do anything.
12463 */
12464 if (FAILED(iResult))
12465 setMachineState(mConsoleTaskData.mLastState);
12466
12467 /* notify the progress object about operation completion */
12468 Assert(mConsoleTaskData.mProgress);
12469 if (SUCCEEDED(iResult))
12470 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12471 else
12472 {
12473 Utf8Str strErrMsg(aErrMsg);
12474 if (strErrMsg.length())
12475 mConsoleTaskData.mProgress->notifyComplete(iResult,
12476 COM_IIDOF(ISession),
12477 getComponentName(),
12478 strErrMsg.c_str());
12479 else
12480 mConsoleTaskData.mProgress->notifyComplete(iResult);
12481 }
12482
12483 /* clear out the temporary saved state data */
12484 mConsoleTaskData.mLastState = MachineState_Null;
12485 mConsoleTaskData.mProgress.setNull();
12486
12487 LogFlowThisFuncLeave();
12488 return S_OK;
12489}
12490
12491
12492/**
12493 * Goes through the USB filters of the given machine to see if the given
12494 * device matches any filter or not.
12495 *
12496 * @note Locks the same as USBController::hasMatchingFilter() does.
12497 */
12498STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12499 BOOL *aMatched,
12500 ULONG *aMaskedIfs)
12501{
12502 LogFlowThisFunc(("\n"));
12503
12504 CheckComArgNotNull(aUSBDevice);
12505 CheckComArgOutPointerValid(aMatched);
12506
12507 AutoCaller autoCaller(this);
12508 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12509
12510#ifdef VBOX_WITH_USB
12511 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12512#else
12513 NOREF(aUSBDevice);
12514 NOREF(aMaskedIfs);
12515 *aMatched = FALSE;
12516#endif
12517
12518 return S_OK;
12519}
12520
12521/**
12522 * @note Locks the same as Host::captureUSBDevice() does.
12523 */
12524STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12525{
12526 LogFlowThisFunc(("\n"));
12527
12528 AutoCaller autoCaller(this);
12529 AssertComRCReturnRC(autoCaller.rc());
12530
12531#ifdef VBOX_WITH_USB
12532 /* if captureDeviceForVM() fails, it must have set extended error info */
12533 clearError();
12534 MultiResult rc = mParent->host()->checkUSBProxyService();
12535 if (FAILED(rc)) return rc;
12536
12537 USBProxyService *service = mParent->host()->usbProxyService();
12538 AssertReturn(service, E_FAIL);
12539 return service->captureDeviceForVM(this, Guid(aId).ref());
12540#else
12541 NOREF(aId);
12542 return E_NOTIMPL;
12543#endif
12544}
12545
12546/**
12547 * @note Locks the same as Host::detachUSBDevice() does.
12548 */
12549STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12550{
12551 LogFlowThisFunc(("\n"));
12552
12553 AutoCaller autoCaller(this);
12554 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12555
12556#ifdef VBOX_WITH_USB
12557 USBProxyService *service = mParent->host()->usbProxyService();
12558 AssertReturn(service, E_FAIL);
12559 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12560#else
12561 NOREF(aId);
12562 NOREF(aDone);
12563 return E_NOTIMPL;
12564#endif
12565}
12566
12567/**
12568 * Inserts all machine filters to the USB proxy service and then calls
12569 * Host::autoCaptureUSBDevices().
12570 *
12571 * Called by Console from the VM process upon VM startup.
12572 *
12573 * @note Locks what called methods lock.
12574 */
12575STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12576{
12577 LogFlowThisFunc(("\n"));
12578
12579 AutoCaller autoCaller(this);
12580 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12581
12582#ifdef VBOX_WITH_USB
12583 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12584 AssertComRC(rc);
12585 NOREF(rc);
12586
12587 USBProxyService *service = mParent->host()->usbProxyService();
12588 AssertReturn(service, E_FAIL);
12589 return service->autoCaptureDevicesForVM(this);
12590#else
12591 return S_OK;
12592#endif
12593}
12594
12595/**
12596 * Removes all machine filters from the USB proxy service and then calls
12597 * Host::detachAllUSBDevices().
12598 *
12599 * Called by Console from the VM process upon normal VM termination or by
12600 * SessionMachine::uninit() upon abnormal VM termination (from under the
12601 * Machine/SessionMachine lock).
12602 *
12603 * @note Locks what called methods lock.
12604 */
12605STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12606{
12607 LogFlowThisFunc(("\n"));
12608
12609 AutoCaller autoCaller(this);
12610 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12611
12612#ifdef VBOX_WITH_USB
12613 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12614 AssertComRC(rc);
12615 NOREF(rc);
12616
12617 USBProxyService *service = mParent->host()->usbProxyService();
12618 AssertReturn(service, E_FAIL);
12619 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12620#else
12621 NOREF(aDone);
12622 return S_OK;
12623#endif
12624}
12625
12626/**
12627 * @note Locks this object for writing.
12628 */
12629STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12630 IProgress **aProgress)
12631{
12632 LogFlowThisFuncEnter();
12633
12634 AssertReturn(aSession, E_INVALIDARG);
12635 AssertReturn(aProgress, E_INVALIDARG);
12636
12637 AutoCaller autoCaller(this);
12638
12639 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12640 /*
12641 * We don't assert below because it might happen that a non-direct session
12642 * informs us it is closed right after we've been uninitialized -- it's ok.
12643 */
12644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12645
12646 /* get IInternalSessionControl interface */
12647 ComPtr<IInternalSessionControl> control(aSession);
12648
12649 ComAssertRet(!control.isNull(), E_INVALIDARG);
12650
12651 /* Creating a Progress object requires the VirtualBox lock, and
12652 * thus locking it here is required by the lock order rules. */
12653 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12654
12655 if (control == mData->mSession.mDirectControl)
12656 {
12657 ComAssertRet(aProgress, E_POINTER);
12658
12659 /* The direct session is being normally closed by the client process
12660 * ----------------------------------------------------------------- */
12661
12662 /* go to the closing state (essential for all open*Session() calls and
12663 * for #checkForDeath()) */
12664 Assert(mData->mSession.mState == SessionState_Locked);
12665 mData->mSession.mState = SessionState_Unlocking;
12666
12667 /* set direct control to NULL to release the remote instance */
12668 mData->mSession.mDirectControl.setNull();
12669 LogFlowThisFunc(("Direct control is set to NULL\n"));
12670
12671 if (mData->mSession.mProgress)
12672 {
12673 /* finalize the progress, someone might wait if a frontend
12674 * closes the session before powering on the VM. */
12675 mData->mSession.mProgress->notifyComplete(E_FAIL,
12676 COM_IIDOF(ISession),
12677 getComponentName(),
12678 tr("The VM session was closed before any attempt to power it on"));
12679 mData->mSession.mProgress.setNull();
12680 }
12681
12682 /* Create the progress object the client will use to wait until
12683 * #checkForDeath() is called to uninitialize this session object after
12684 * it releases the IPC semaphore.
12685 * Note! Because we're "reusing" mProgress here, this must be a proxy
12686 * object just like for LaunchVMProcess. */
12687 Assert(mData->mSession.mProgress.isNull());
12688 ComObjPtr<ProgressProxy> progress;
12689 progress.createObject();
12690 ComPtr<IUnknown> pPeer(mPeer);
12691 progress->init(mParent, pPeer,
12692 Bstr(tr("Closing session")).raw(),
12693 FALSE /* aCancelable */);
12694 progress.queryInterfaceTo(aProgress);
12695 mData->mSession.mProgress = progress;
12696 }
12697 else
12698 {
12699 /* the remote session is being normally closed */
12700 Data::Session::RemoteControlList::iterator it =
12701 mData->mSession.mRemoteControls.begin();
12702 while (it != mData->mSession.mRemoteControls.end())
12703 {
12704 if (control == *it)
12705 break;
12706 ++it;
12707 }
12708 BOOL found = it != mData->mSession.mRemoteControls.end();
12709 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12710 E_INVALIDARG);
12711 // This MUST be erase(it), not remove(*it) as the latter triggers a
12712 // very nasty use after free due to the place where the value "lives".
12713 mData->mSession.mRemoteControls.erase(it);
12714 }
12715
12716 /* signal the client watcher thread, because the client is going away */
12717 mParent->updateClientWatcher();
12718
12719 LogFlowThisFuncLeave();
12720 return S_OK;
12721}
12722
12723/**
12724 * @note Locks this object for writing.
12725 */
12726STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12727{
12728 LogFlowThisFuncEnter();
12729
12730 CheckComArgOutPointerValid(aProgress);
12731 CheckComArgOutPointerValid(aStateFilePath);
12732
12733 AutoCaller autoCaller(this);
12734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12735
12736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12737
12738 AssertReturn( mData->mMachineState == MachineState_Paused
12739 && mConsoleTaskData.mLastState == MachineState_Null
12740 && mConsoleTaskData.strStateFilePath.isEmpty(),
12741 E_FAIL);
12742
12743 /* create a progress object to track operation completion */
12744 ComObjPtr<Progress> pProgress;
12745 pProgress.createObject();
12746 pProgress->init(getVirtualBox(),
12747 static_cast<IMachine *>(this) /* aInitiator */,
12748 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12749 FALSE /* aCancelable */);
12750
12751 Utf8Str strStateFilePath;
12752 /* stateFilePath is null when the machine is not running */
12753 if (mData->mMachineState == MachineState_Paused)
12754 composeSavedStateFilename(strStateFilePath);
12755
12756 /* fill in the console task data */
12757 mConsoleTaskData.mLastState = mData->mMachineState;
12758 mConsoleTaskData.strStateFilePath = strStateFilePath;
12759 mConsoleTaskData.mProgress = pProgress;
12760
12761 /* set the state to Saving (this is expected by Console::SaveState()) */
12762 setMachineState(MachineState_Saving);
12763
12764 strStateFilePath.cloneTo(aStateFilePath);
12765 pProgress.queryInterfaceTo(aProgress);
12766
12767 return S_OK;
12768}
12769
12770/**
12771 * @note Locks mParent + this object for writing.
12772 */
12773STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12774{
12775 LogFlowThisFunc(("\n"));
12776
12777 AutoCaller autoCaller(this);
12778 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12779
12780 /* endSavingState() need mParent lock */
12781 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12782
12783 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12784 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12785 && mConsoleTaskData.mLastState != MachineState_Null
12786 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12787 E_FAIL);
12788
12789 /*
12790 * On failure, set the state to the state we had when BeginSavingState()
12791 * was called (this is expected by Console::SaveState() and the associated
12792 * task). On success the VM process already changed the state to
12793 * MachineState_Saved, so no need to do anything.
12794 */
12795 if (FAILED(iResult))
12796 setMachineState(mConsoleTaskData.mLastState);
12797
12798 return endSavingState(iResult, aErrMsg);
12799}
12800
12801/**
12802 * @note Locks this object for writing.
12803 */
12804STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12805{
12806 LogFlowThisFunc(("\n"));
12807
12808 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12809
12810 AutoCaller autoCaller(this);
12811 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12812
12813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12814
12815 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12816 || mData->mMachineState == MachineState_Teleported
12817 || mData->mMachineState == MachineState_Aborted
12818 , E_FAIL); /** @todo setError. */
12819
12820 Utf8Str stateFilePathFull = aSavedStateFile;
12821 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12822 if (RT_FAILURE(vrc))
12823 return setError(VBOX_E_FILE_ERROR,
12824 tr("Invalid saved state file path '%ls' (%Rrc)"),
12825 aSavedStateFile,
12826 vrc);
12827
12828 mSSData->strStateFilePath = stateFilePathFull;
12829
12830 /* The below setMachineState() will detect the state transition and will
12831 * update the settings file */
12832
12833 return setMachineState(MachineState_Saved);
12834}
12835
12836STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12837 ComSafeArrayOut(BSTR, aValues),
12838 ComSafeArrayOut(LONG64, aTimestamps),
12839 ComSafeArrayOut(BSTR, aFlags))
12840{
12841 LogFlowThisFunc(("\n"));
12842
12843#ifdef VBOX_WITH_GUEST_PROPS
12844 using namespace guestProp;
12845
12846 AutoCaller autoCaller(this);
12847 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12848
12849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12850
12851 CheckComArgOutSafeArrayPointerValid(aNames);
12852 CheckComArgOutSafeArrayPointerValid(aValues);
12853 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12854 CheckComArgOutSafeArrayPointerValid(aFlags);
12855
12856 size_t cEntries = mHWData->mGuestProperties.size();
12857 com::SafeArray<BSTR> names(cEntries);
12858 com::SafeArray<BSTR> values(cEntries);
12859 com::SafeArray<LONG64> timestamps(cEntries);
12860 com::SafeArray<BSTR> flags(cEntries);
12861 unsigned i = 0;
12862 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12863 it != mHWData->mGuestProperties.end();
12864 ++it)
12865 {
12866 char szFlags[MAX_FLAGS_LEN + 1];
12867 it->first.cloneTo(&names[i]);
12868 it->second.strValue.cloneTo(&values[i]);
12869 timestamps[i] = it->second.mTimestamp;
12870 /* If it is NULL, keep it NULL. */
12871 if (it->second.mFlags)
12872 {
12873 writeFlags(it->second.mFlags, szFlags);
12874 Bstr(szFlags).cloneTo(&flags[i]);
12875 }
12876 else
12877 flags[i] = NULL;
12878 ++i;
12879 }
12880 names.detachTo(ComSafeArrayOutArg(aNames));
12881 values.detachTo(ComSafeArrayOutArg(aValues));
12882 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12883 flags.detachTo(ComSafeArrayOutArg(aFlags));
12884 return S_OK;
12885#else
12886 ReturnComNotImplemented();
12887#endif
12888}
12889
12890STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12891 IN_BSTR aValue,
12892 LONG64 aTimestamp,
12893 IN_BSTR aFlags)
12894{
12895 LogFlowThisFunc(("\n"));
12896
12897#ifdef VBOX_WITH_GUEST_PROPS
12898 using namespace guestProp;
12899
12900 CheckComArgStrNotEmptyOrNull(aName);
12901 CheckComArgNotNull(aValue);
12902 CheckComArgNotNull(aFlags);
12903
12904 try
12905 {
12906 /*
12907 * Convert input up front.
12908 */
12909 Utf8Str utf8Name(aName);
12910 uint32_t fFlags = NILFLAG;
12911 if (aFlags)
12912 {
12913 Utf8Str utf8Flags(aFlags);
12914 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12915 AssertRCReturn(vrc, E_INVALIDARG);
12916 }
12917
12918 /*
12919 * Now grab the object lock, validate the state and do the update.
12920 */
12921 AutoCaller autoCaller(this);
12922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12923
12924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12925
12926 switch (mData->mMachineState)
12927 {
12928 case MachineState_Paused:
12929 case MachineState_Running:
12930 case MachineState_Teleporting:
12931 case MachineState_TeleportingPausedVM:
12932 case MachineState_LiveSnapshotting:
12933 case MachineState_DeletingSnapshotOnline:
12934 case MachineState_DeletingSnapshotPaused:
12935 case MachineState_Saving:
12936 case MachineState_Stopping:
12937 break;
12938
12939 default:
12940 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12941 VBOX_E_INVALID_VM_STATE);
12942 }
12943
12944 setModified(IsModified_MachineData);
12945 mHWData.backup();
12946
12947 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
12948 if (it != mHWData->mGuestProperties.end())
12949 {
12950 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
12951 {
12952 it->second.strValue = aValue;
12953 it->second.mFlags = fFlags;
12954 it->second.mTimestamp = aTimestamp;
12955 }
12956 else
12957 mHWData->mGuestProperties.erase(it);
12958
12959 mData->mGuestPropertiesModified = TRUE;
12960 }
12961
12962 /*
12963 * Send a callback notification if appropriate
12964 */
12965 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12966 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12967 RTSTR_MAX,
12968 utf8Name.c_str(),
12969 RTSTR_MAX, NULL)
12970 )
12971 {
12972 alock.release();
12973
12974 mParent->onGuestPropertyChange(mData->mUuid,
12975 aName,
12976 aValue,
12977 aFlags);
12978 }
12979 }
12980 catch (...)
12981 {
12982 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12983 }
12984 return S_OK;
12985#else
12986 ReturnComNotImplemented();
12987#endif
12988}
12989
12990STDMETHODIMP SessionMachine::LockMedia()
12991{
12992 AutoCaller autoCaller(this);
12993 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12994
12995 AutoMultiWriteLock2 alock(this->lockHandle(),
12996 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12997
12998 AssertReturn( mData->mMachineState == MachineState_Starting
12999 || mData->mMachineState == MachineState_Restoring
13000 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13001
13002 clearError();
13003 alock.release();
13004 return lockMedia();
13005}
13006
13007STDMETHODIMP SessionMachine::UnlockMedia()
13008{
13009 unlockMedia();
13010 return S_OK;
13011}
13012
13013STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13014 IMediumAttachment **aNewAttachment)
13015{
13016 CheckComArgNotNull(aAttachment);
13017 CheckComArgOutPointerValid(aNewAttachment);
13018
13019 AutoCaller autoCaller(this);
13020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13021
13022 // request the host lock first, since might be calling Host methods for getting host drives;
13023 // next, protect the media tree all the while we're in here, as well as our member variables
13024 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13025 this->lockHandle(),
13026 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13027
13028 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13029
13030 Bstr ctrlName;
13031 LONG lPort;
13032 LONG lDevice;
13033 bool fTempEject;
13034 {
13035 AutoCaller autoAttachCaller(this);
13036 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13037
13038 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13039
13040 /* Need to query the details first, as the IMediumAttachment reference
13041 * might be to the original settings, which we are going to change. */
13042 ctrlName = pAttach->getControllerName();
13043 lPort = pAttach->getPort();
13044 lDevice = pAttach->getDevice();
13045 fTempEject = pAttach->getTempEject();
13046 }
13047
13048 if (!fTempEject)
13049 {
13050 /* Remember previously mounted medium. The medium before taking the
13051 * backup is not necessarily the same thing. */
13052 ComObjPtr<Medium> oldmedium;
13053 oldmedium = pAttach->getMedium();
13054
13055 setModified(IsModified_Storage);
13056 mMediaData.backup();
13057
13058 // The backup operation makes the pAttach reference point to the
13059 // old settings. Re-get the correct reference.
13060 pAttach = findAttachment(mMediaData->mAttachments,
13061 ctrlName.raw(),
13062 lPort,
13063 lDevice);
13064
13065 {
13066 AutoCaller autoAttachCaller(this);
13067 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13068
13069 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13070 if (!oldmedium.isNull())
13071 oldmedium->removeBackReference(mData->mUuid);
13072
13073 pAttach->updateMedium(NULL);
13074 pAttach->updateEjected();
13075 }
13076
13077 setModified(IsModified_Storage);
13078 }
13079 else
13080 {
13081 {
13082 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13083 pAttach->updateEjected();
13084 }
13085 }
13086
13087 pAttach.queryInterfaceTo(aNewAttachment);
13088
13089 return S_OK;
13090}
13091
13092// public methods only for internal purposes
13093/////////////////////////////////////////////////////////////////////////////
13094
13095/**
13096 * Called from the client watcher thread to check for expected or unexpected
13097 * death of the client process that has a direct session to this machine.
13098 *
13099 * On Win32 and on OS/2, this method is called only when we've got the
13100 * mutex (i.e. the client has either died or terminated normally) so it always
13101 * returns @c true (the client is terminated, the session machine is
13102 * uninitialized).
13103 *
13104 * On other platforms, the method returns @c true if the client process has
13105 * terminated normally or abnormally and the session machine was uninitialized,
13106 * and @c false if the client process is still alive.
13107 *
13108 * @note Locks this object for writing.
13109 */
13110bool SessionMachine::checkForDeath()
13111{
13112 Uninit::Reason reason;
13113 bool terminated = false;
13114
13115 /* Enclose autoCaller with a block because calling uninit() from under it
13116 * will deadlock. */
13117 {
13118 AutoCaller autoCaller(this);
13119 if (!autoCaller.isOk())
13120 {
13121 /* return true if not ready, to cause the client watcher to exclude
13122 * the corresponding session from watching */
13123 LogFlowThisFunc(("Already uninitialized!\n"));
13124 return true;
13125 }
13126
13127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13128
13129 /* Determine the reason of death: if the session state is Closing here,
13130 * everything is fine. Otherwise it means that the client did not call
13131 * OnSessionEnd() before it released the IPC semaphore. This may happen
13132 * either because the client process has abnormally terminated, or
13133 * because it simply forgot to call ISession::Close() before exiting. We
13134 * threat the latter also as an abnormal termination (see
13135 * Session::uninit() for details). */
13136 reason = mData->mSession.mState == SessionState_Unlocking ?
13137 Uninit::Normal :
13138 Uninit::Abnormal;
13139
13140#if defined(RT_OS_WINDOWS)
13141
13142 AssertMsg(mIPCSem, ("semaphore must be created"));
13143
13144 /* release the IPC mutex */
13145 ::ReleaseMutex(mIPCSem);
13146
13147 terminated = true;
13148
13149#elif defined(RT_OS_OS2)
13150
13151 AssertMsg(mIPCSem, ("semaphore must be created"));
13152
13153 /* release the IPC mutex */
13154 ::DosReleaseMutexSem(mIPCSem);
13155
13156 terminated = true;
13157
13158#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13159
13160 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13161
13162 int val = ::semctl(mIPCSem, 0, GETVAL);
13163 if (val > 0)
13164 {
13165 /* the semaphore is signaled, meaning the session is terminated */
13166 terminated = true;
13167 }
13168
13169#else
13170# error "Port me!"
13171#endif
13172
13173 } /* AutoCaller block */
13174
13175 if (terminated)
13176 uninit(reason);
13177
13178 return terminated;
13179}
13180
13181/**
13182 * @note Locks this object for reading.
13183 */
13184HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13185{
13186 LogFlowThisFunc(("\n"));
13187
13188 AutoCaller autoCaller(this);
13189 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13190
13191 ComPtr<IInternalSessionControl> directControl;
13192 {
13193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13194 directControl = mData->mSession.mDirectControl;
13195 }
13196
13197 /* ignore notifications sent after #OnSessionEnd() is called */
13198 if (!directControl)
13199 return S_OK;
13200
13201 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13202}
13203
13204/**
13205 * @note Locks this object for reading.
13206 */
13207HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13208 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13209{
13210 LogFlowThisFunc(("\n"));
13211
13212 AutoCaller autoCaller(this);
13213 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13214
13215 ComPtr<IInternalSessionControl> directControl;
13216 {
13217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13218 directControl = mData->mSession.mDirectControl;
13219 }
13220
13221 /* ignore notifications sent after #OnSessionEnd() is called */
13222 if (!directControl)
13223 return S_OK;
13224 /*
13225 * instead acting like callback we ask IVirtualBox deliver corresponding event
13226 */
13227
13228 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13229 return S_OK;
13230}
13231
13232/**
13233 * @note Locks this object for reading.
13234 */
13235HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13236{
13237 LogFlowThisFunc(("\n"));
13238
13239 AutoCaller autoCaller(this);
13240 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13241
13242 ComPtr<IInternalSessionControl> directControl;
13243 {
13244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13245 directControl = mData->mSession.mDirectControl;
13246 }
13247
13248 /* ignore notifications sent after #OnSessionEnd() is called */
13249 if (!directControl)
13250 return S_OK;
13251
13252 return directControl->OnSerialPortChange(serialPort);
13253}
13254
13255/**
13256 * @note Locks this object for reading.
13257 */
13258HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13259{
13260 LogFlowThisFunc(("\n"));
13261
13262 AutoCaller autoCaller(this);
13263 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13264
13265 ComPtr<IInternalSessionControl> directControl;
13266 {
13267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13268 directControl = mData->mSession.mDirectControl;
13269 }
13270
13271 /* ignore notifications sent after #OnSessionEnd() is called */
13272 if (!directControl)
13273 return S_OK;
13274
13275 return directControl->OnParallelPortChange(parallelPort);
13276}
13277
13278/**
13279 * @note Locks this object for reading.
13280 */
13281HRESULT SessionMachine::onStorageControllerChange()
13282{
13283 LogFlowThisFunc(("\n"));
13284
13285 AutoCaller autoCaller(this);
13286 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13287
13288 ComPtr<IInternalSessionControl> directControl;
13289 {
13290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13291 directControl = mData->mSession.mDirectControl;
13292 }
13293
13294 /* ignore notifications sent after #OnSessionEnd() is called */
13295 if (!directControl)
13296 return S_OK;
13297
13298 return directControl->OnStorageControllerChange();
13299}
13300
13301/**
13302 * @note Locks this object for reading.
13303 */
13304HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13305{
13306 LogFlowThisFunc(("\n"));
13307
13308 AutoCaller autoCaller(this);
13309 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13310
13311 ComPtr<IInternalSessionControl> directControl;
13312 {
13313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13314 directControl = mData->mSession.mDirectControl;
13315 }
13316
13317 /* ignore notifications sent after #OnSessionEnd() is called */
13318 if (!directControl)
13319 return S_OK;
13320
13321 return directControl->OnMediumChange(aAttachment, aForce);
13322}
13323
13324/**
13325 * @note Locks this object for reading.
13326 */
13327HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13328{
13329 LogFlowThisFunc(("\n"));
13330
13331 AutoCaller autoCaller(this);
13332 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13333
13334 ComPtr<IInternalSessionControl> directControl;
13335 {
13336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13337 directControl = mData->mSession.mDirectControl;
13338 }
13339
13340 /* ignore notifications sent after #OnSessionEnd() is called */
13341 if (!directControl)
13342 return S_OK;
13343
13344 return directControl->OnCPUChange(aCPU, aRemove);
13345}
13346
13347HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13348{
13349 LogFlowThisFunc(("\n"));
13350
13351 AutoCaller autoCaller(this);
13352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13353
13354 ComPtr<IInternalSessionControl> directControl;
13355 {
13356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13357 directControl = mData->mSession.mDirectControl;
13358 }
13359
13360 /* ignore notifications sent after #OnSessionEnd() is called */
13361 if (!directControl)
13362 return S_OK;
13363
13364 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13365}
13366
13367/**
13368 * @note Locks this object for reading.
13369 */
13370HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13371{
13372 LogFlowThisFunc(("\n"));
13373
13374 AutoCaller autoCaller(this);
13375 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13376
13377 ComPtr<IInternalSessionControl> directControl;
13378 {
13379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13380 directControl = mData->mSession.mDirectControl;
13381 }
13382
13383 /* ignore notifications sent after #OnSessionEnd() is called */
13384 if (!directControl)
13385 return S_OK;
13386
13387 return directControl->OnVRDEServerChange(aRestart);
13388}
13389
13390/**
13391 * @note Locks this object for reading.
13392 */
13393HRESULT SessionMachine::onUSBControllerChange()
13394{
13395 LogFlowThisFunc(("\n"));
13396
13397 AutoCaller autoCaller(this);
13398 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13399
13400 ComPtr<IInternalSessionControl> directControl;
13401 {
13402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13403 directControl = mData->mSession.mDirectControl;
13404 }
13405
13406 /* ignore notifications sent after #OnSessionEnd() is called */
13407 if (!directControl)
13408 return S_OK;
13409
13410 return directControl->OnUSBControllerChange();
13411}
13412
13413/**
13414 * @note Locks this object for reading.
13415 */
13416HRESULT SessionMachine::onSharedFolderChange()
13417{
13418 LogFlowThisFunc(("\n"));
13419
13420 AutoCaller autoCaller(this);
13421 AssertComRCReturnRC(autoCaller.rc());
13422
13423 ComPtr<IInternalSessionControl> directControl;
13424 {
13425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13426 directControl = mData->mSession.mDirectControl;
13427 }
13428
13429 /* ignore notifications sent after #OnSessionEnd() is called */
13430 if (!directControl)
13431 return S_OK;
13432
13433 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13434}
13435
13436/**
13437 * @note Locks this object for reading.
13438 */
13439HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13440{
13441 LogFlowThisFunc(("\n"));
13442
13443 AutoCaller autoCaller(this);
13444 AssertComRCReturnRC(autoCaller.rc());
13445
13446 ComPtr<IInternalSessionControl> directControl;
13447 {
13448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13449 directControl = mData->mSession.mDirectControl;
13450 }
13451
13452 /* ignore notifications sent after #OnSessionEnd() is called */
13453 if (!directControl)
13454 return S_OK;
13455
13456 return directControl->OnClipboardModeChange(aClipboardMode);
13457}
13458
13459/**
13460 * @note Locks this object for reading.
13461 */
13462HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13463{
13464 LogFlowThisFunc(("\n"));
13465
13466 AutoCaller autoCaller(this);
13467 AssertComRCReturnRC(autoCaller.rc());
13468
13469 ComPtr<IInternalSessionControl> directControl;
13470 {
13471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13472 directControl = mData->mSession.mDirectControl;
13473 }
13474
13475 /* ignore notifications sent after #OnSessionEnd() is called */
13476 if (!directControl)
13477 return S_OK;
13478
13479 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13480}
13481
13482/**
13483 * @note Locks this object for reading.
13484 */
13485HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13486{
13487 LogFlowThisFunc(("\n"));
13488
13489 AutoCaller autoCaller(this);
13490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13491
13492 ComPtr<IInternalSessionControl> directControl;
13493 {
13494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13495 directControl = mData->mSession.mDirectControl;
13496 }
13497
13498 /* ignore notifications sent after #OnSessionEnd() is called */
13499 if (!directControl)
13500 return S_OK;
13501
13502 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13503}
13504
13505/**
13506 * @note Locks this object for reading.
13507 */
13508HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512 AutoCaller autoCaller(this);
13513 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13514
13515 ComPtr<IInternalSessionControl> directControl;
13516 {
13517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13518 directControl = mData->mSession.mDirectControl;
13519 }
13520
13521 /* ignore notifications sent after #OnSessionEnd() is called */
13522 if (!directControl)
13523 return S_OK;
13524
13525 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13526}
13527
13528/**
13529 * Returns @c true if this machine's USB controller reports it has a matching
13530 * filter for the given USB device and @c false otherwise.
13531 *
13532 * @note locks this object for reading.
13533 */
13534bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13535{
13536 AutoCaller autoCaller(this);
13537 /* silently return if not ready -- this method may be called after the
13538 * direct machine session has been called */
13539 if (!autoCaller.isOk())
13540 return false;
13541
13542#ifdef VBOX_WITH_USB
13543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13544
13545 switch (mData->mMachineState)
13546 {
13547 case MachineState_Starting:
13548 case MachineState_Restoring:
13549 case MachineState_TeleportingIn:
13550 case MachineState_Paused:
13551 case MachineState_Running:
13552 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13553 * elsewhere... */
13554 alock.release();
13555 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13556 default: break;
13557 }
13558#else
13559 NOREF(aDevice);
13560 NOREF(aMaskedIfs);
13561#endif
13562 return false;
13563}
13564
13565/**
13566 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13567 */
13568HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13569 IVirtualBoxErrorInfo *aError,
13570 ULONG aMaskedIfs)
13571{
13572 LogFlowThisFunc(("\n"));
13573
13574 AutoCaller autoCaller(this);
13575
13576 /* This notification may happen after the machine object has been
13577 * uninitialized (the session was closed), so don't assert. */
13578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13579
13580 ComPtr<IInternalSessionControl> directControl;
13581 {
13582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13583 directControl = mData->mSession.mDirectControl;
13584 }
13585
13586 /* fail on notifications sent after #OnSessionEnd() is called, it is
13587 * expected by the caller */
13588 if (!directControl)
13589 return E_FAIL;
13590
13591 /* No locks should be held at this point. */
13592 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13593 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13594
13595 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13596}
13597
13598/**
13599 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13600 */
13601HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13602 IVirtualBoxErrorInfo *aError)
13603{
13604 LogFlowThisFunc(("\n"));
13605
13606 AutoCaller autoCaller(this);
13607
13608 /* This notification may happen after the machine object has been
13609 * uninitialized (the session was closed), so don't assert. */
13610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13611
13612 ComPtr<IInternalSessionControl> directControl;
13613 {
13614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13615 directControl = mData->mSession.mDirectControl;
13616 }
13617
13618 /* fail on notifications sent after #OnSessionEnd() is called, it is
13619 * expected by the caller */
13620 if (!directControl)
13621 return E_FAIL;
13622
13623 /* No locks should be held at this point. */
13624 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13625 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13626
13627 return directControl->OnUSBDeviceDetach(aId, aError);
13628}
13629
13630// protected methods
13631/////////////////////////////////////////////////////////////////////////////
13632
13633/**
13634 * Helper method to finalize saving the state.
13635 *
13636 * @note Must be called from under this object's lock.
13637 *
13638 * @param aRc S_OK if the snapshot has been taken successfully
13639 * @param aErrMsg human readable error message for failure
13640 *
13641 * @note Locks mParent + this objects for writing.
13642 */
13643HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13644{
13645 LogFlowThisFuncEnter();
13646
13647 AutoCaller autoCaller(this);
13648 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13649
13650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13651
13652 HRESULT rc = S_OK;
13653
13654 if (SUCCEEDED(aRc))
13655 {
13656 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13657
13658 /* save all VM settings */
13659 rc = saveSettings(NULL);
13660 // no need to check whether VirtualBox.xml needs saving also since
13661 // we can't have a name change pending at this point
13662 }
13663 else
13664 {
13665 // delete the saved state file (it might have been already created);
13666 // we need not check whether this is shared with a snapshot here because
13667 // we certainly created this saved state file here anew
13668 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13669 }
13670
13671 /* notify the progress object about operation completion */
13672 Assert(mConsoleTaskData.mProgress);
13673 if (SUCCEEDED(aRc))
13674 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13675 else
13676 {
13677 if (aErrMsg.length())
13678 mConsoleTaskData.mProgress->notifyComplete(aRc,
13679 COM_IIDOF(ISession),
13680 getComponentName(),
13681 aErrMsg.c_str());
13682 else
13683 mConsoleTaskData.mProgress->notifyComplete(aRc);
13684 }
13685
13686 /* clear out the temporary saved state data */
13687 mConsoleTaskData.mLastState = MachineState_Null;
13688 mConsoleTaskData.strStateFilePath.setNull();
13689 mConsoleTaskData.mProgress.setNull();
13690
13691 LogFlowThisFuncLeave();
13692 return rc;
13693}
13694
13695/**
13696 * Deletes the given file if it is no longer in use by either the current machine state
13697 * (if the machine is "saved") or any of the machine's snapshots.
13698 *
13699 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13700 * but is different for each SnapshotMachine. When calling this, the order of calling this
13701 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13702 * is therefore critical. I know, it's all rather messy.
13703 *
13704 * @param strStateFile
13705 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13706 */
13707void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13708 Snapshot *pSnapshotToIgnore)
13709{
13710 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13711 if ( (strStateFile.isNotEmpty())
13712 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13713 )
13714 // ... and it must also not be shared with other snapshots
13715 if ( !mData->mFirstSnapshot
13716 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13717 // this checks the SnapshotMachine's state file paths
13718 )
13719 RTFileDelete(strStateFile.c_str());
13720}
13721
13722/**
13723 * Locks the attached media.
13724 *
13725 * All attached hard disks are locked for writing and DVD/floppy are locked for
13726 * reading. Parents of attached hard disks (if any) are locked for reading.
13727 *
13728 * This method also performs accessibility check of all media it locks: if some
13729 * media is inaccessible, the method will return a failure and a bunch of
13730 * extended error info objects per each inaccessible medium.
13731 *
13732 * Note that this method is atomic: if it returns a success, all media are
13733 * locked as described above; on failure no media is locked at all (all
13734 * succeeded individual locks will be undone).
13735 *
13736 * The caller is responsible for doing the necessary state sanity checks.
13737 *
13738 * The locks made by this method must be undone by calling #unlockMedia() when
13739 * no more needed.
13740 */
13741HRESULT SessionMachine::lockMedia()
13742{
13743 AutoCaller autoCaller(this);
13744 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13745
13746 AutoMultiWriteLock2 alock(this->lockHandle(),
13747 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13748
13749 /* bail out if trying to lock things with already set up locking */
13750 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13751
13752 MultiResult mrc(S_OK);
13753
13754 /* Collect locking information for all medium objects attached to the VM. */
13755 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13756 it != mMediaData->mAttachments.end();
13757 ++it)
13758 {
13759 MediumAttachment* pAtt = *it;
13760 DeviceType_T devType = pAtt->getType();
13761 Medium *pMedium = pAtt->getMedium();
13762
13763 MediumLockList *pMediumLockList(new MediumLockList());
13764 // There can be attachments without a medium (floppy/dvd), and thus
13765 // it's impossible to create a medium lock list. It still makes sense
13766 // to have the empty medium lock list in the map in case a medium is
13767 // attached later.
13768 if (pMedium != NULL)
13769 {
13770 MediumType_T mediumType = pMedium->getType();
13771 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13772 || mediumType == MediumType_Shareable;
13773 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13774
13775 alock.release();
13776 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13777 !fIsReadOnlyLock /* fMediumLockWrite */,
13778 NULL,
13779 *pMediumLockList);
13780 alock.acquire();
13781 if (FAILED(mrc))
13782 {
13783 delete pMediumLockList;
13784 mData->mSession.mLockedMedia.Clear();
13785 break;
13786 }
13787 }
13788
13789 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13790 if (FAILED(rc))
13791 {
13792 mData->mSession.mLockedMedia.Clear();
13793 mrc = setError(rc,
13794 tr("Collecting locking information for all attached media failed"));
13795 break;
13796 }
13797 }
13798
13799 if (SUCCEEDED(mrc))
13800 {
13801 /* Now lock all media. If this fails, nothing is locked. */
13802 alock.release();
13803 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13804 alock.acquire();
13805 if (FAILED(rc))
13806 {
13807 mrc = setError(rc,
13808 tr("Locking of attached media failed"));
13809 }
13810 }
13811
13812 return mrc;
13813}
13814
13815/**
13816 * Undoes the locks made by by #lockMedia().
13817 */
13818void SessionMachine::unlockMedia()
13819{
13820 AutoCaller autoCaller(this);
13821 AssertComRCReturnVoid(autoCaller.rc());
13822
13823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13824
13825 /* we may be holding important error info on the current thread;
13826 * preserve it */
13827 ErrorInfoKeeper eik;
13828
13829 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13830 AssertComRC(rc);
13831}
13832
13833/**
13834 * Helper to change the machine state (reimplementation).
13835 *
13836 * @note Locks this object for writing.
13837 * @note This method must not call saveSettings or SaveSettings, otherwise
13838 * it can cause crashes in random places due to unexpectedly committing
13839 * the current settings. The caller is responsible for that. The call
13840 * to saveStateSettings is fine, because this method does not commit.
13841 */
13842HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13843{
13844 LogFlowThisFuncEnter();
13845 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13846
13847 AutoCaller autoCaller(this);
13848 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13849
13850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13851
13852 MachineState_T oldMachineState = mData->mMachineState;
13853
13854 AssertMsgReturn(oldMachineState != aMachineState,
13855 ("oldMachineState=%s, aMachineState=%s\n",
13856 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13857 E_FAIL);
13858
13859 HRESULT rc = S_OK;
13860
13861 int stsFlags = 0;
13862 bool deleteSavedState = false;
13863
13864 /* detect some state transitions */
13865
13866 if ( ( oldMachineState == MachineState_Saved
13867 && aMachineState == MachineState_Restoring)
13868 || ( ( oldMachineState == MachineState_PoweredOff
13869 || oldMachineState == MachineState_Teleported
13870 || oldMachineState == MachineState_Aborted
13871 )
13872 && ( aMachineState == MachineState_TeleportingIn
13873 || aMachineState == MachineState_Starting
13874 )
13875 )
13876 )
13877 {
13878 /* The EMT thread is about to start */
13879
13880 /* Nothing to do here for now... */
13881
13882 /// @todo NEWMEDIA don't let mDVDDrive and other children
13883 /// change anything when in the Starting/Restoring state
13884 }
13885 else if ( ( oldMachineState == MachineState_Running
13886 || oldMachineState == MachineState_Paused
13887 || oldMachineState == MachineState_Teleporting
13888 || oldMachineState == MachineState_LiveSnapshotting
13889 || oldMachineState == MachineState_Stuck
13890 || oldMachineState == MachineState_Starting
13891 || oldMachineState == MachineState_Stopping
13892 || oldMachineState == MachineState_Saving
13893 || oldMachineState == MachineState_Restoring
13894 || oldMachineState == MachineState_TeleportingPausedVM
13895 || oldMachineState == MachineState_TeleportingIn
13896 )
13897 && ( aMachineState == MachineState_PoweredOff
13898 || aMachineState == MachineState_Saved
13899 || aMachineState == MachineState_Teleported
13900 || aMachineState == MachineState_Aborted
13901 )
13902 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13903 * snapshot */
13904 && ( mConsoleTaskData.mSnapshot.isNull()
13905 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13906 )
13907 )
13908 {
13909 /* The EMT thread has just stopped, unlock attached media. Note that as
13910 * opposed to locking that is done from Console, we do unlocking here
13911 * because the VM process may have aborted before having a chance to
13912 * properly unlock all media it locked. */
13913
13914 unlockMedia();
13915 }
13916
13917 if (oldMachineState == MachineState_Restoring)
13918 {
13919 if (aMachineState != MachineState_Saved)
13920 {
13921 /*
13922 * delete the saved state file once the machine has finished
13923 * restoring from it (note that Console sets the state from
13924 * Restoring to Saved if the VM couldn't restore successfully,
13925 * to give the user an ability to fix an error and retry --
13926 * we keep the saved state file in this case)
13927 */
13928 deleteSavedState = true;
13929 }
13930 }
13931 else if ( oldMachineState == MachineState_Saved
13932 && ( aMachineState == MachineState_PoweredOff
13933 || aMachineState == MachineState_Aborted
13934 || aMachineState == MachineState_Teleported
13935 )
13936 )
13937 {
13938 /*
13939 * delete the saved state after Console::ForgetSavedState() is called
13940 * or if the VM process (owning a direct VM session) crashed while the
13941 * VM was Saved
13942 */
13943
13944 /// @todo (dmik)
13945 // Not sure that deleting the saved state file just because of the
13946 // client death before it attempted to restore the VM is a good
13947 // thing. But when it crashes we need to go to the Aborted state
13948 // which cannot have the saved state file associated... The only
13949 // way to fix this is to make the Aborted condition not a VM state
13950 // but a bool flag: i.e., when a crash occurs, set it to true and
13951 // change the state to PoweredOff or Saved depending on the
13952 // saved state presence.
13953
13954 deleteSavedState = true;
13955 mData->mCurrentStateModified = TRUE;
13956 stsFlags |= SaveSTS_CurStateModified;
13957 }
13958
13959 if ( aMachineState == MachineState_Starting
13960 || aMachineState == MachineState_Restoring
13961 || aMachineState == MachineState_TeleportingIn
13962 )
13963 {
13964 /* set the current state modified flag to indicate that the current
13965 * state is no more identical to the state in the
13966 * current snapshot */
13967 if (!mData->mCurrentSnapshot.isNull())
13968 {
13969 mData->mCurrentStateModified = TRUE;
13970 stsFlags |= SaveSTS_CurStateModified;
13971 }
13972 }
13973
13974 if (deleteSavedState)
13975 {
13976 if (mRemoveSavedState)
13977 {
13978 Assert(!mSSData->strStateFilePath.isEmpty());
13979
13980 // it is safe to delete the saved state file if ...
13981 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13982 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13983 // ... none of the snapshots share the saved state file
13984 )
13985 RTFileDelete(mSSData->strStateFilePath.c_str());
13986 }
13987
13988 mSSData->strStateFilePath.setNull();
13989 stsFlags |= SaveSTS_StateFilePath;
13990 }
13991
13992 /* redirect to the underlying peer machine */
13993 mPeer->setMachineState(aMachineState);
13994
13995 if ( aMachineState == MachineState_PoweredOff
13996 || aMachineState == MachineState_Teleported
13997 || aMachineState == MachineState_Aborted
13998 || aMachineState == MachineState_Saved)
13999 {
14000 /* the machine has stopped execution
14001 * (or the saved state file was adopted) */
14002 stsFlags |= SaveSTS_StateTimeStamp;
14003 }
14004
14005 if ( ( oldMachineState == MachineState_PoweredOff
14006 || oldMachineState == MachineState_Aborted
14007 || oldMachineState == MachineState_Teleported
14008 )
14009 && aMachineState == MachineState_Saved)
14010 {
14011 /* the saved state file was adopted */
14012 Assert(!mSSData->strStateFilePath.isEmpty());
14013 stsFlags |= SaveSTS_StateFilePath;
14014 }
14015
14016#ifdef VBOX_WITH_GUEST_PROPS
14017 if ( aMachineState == MachineState_PoweredOff
14018 || aMachineState == MachineState_Aborted
14019 || aMachineState == MachineState_Teleported)
14020 {
14021 /* Make sure any transient guest properties get removed from the
14022 * property store on shutdown. */
14023
14024 HWData::GuestPropertyMap::const_iterator it;
14025 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14026 if (!fNeedsSaving)
14027 for (it = mHWData->mGuestProperties.begin();
14028 it != mHWData->mGuestProperties.end(); ++it)
14029 if ( (it->second.mFlags & guestProp::TRANSIENT)
14030 || (it->second.mFlags & guestProp::TRANSRESET))
14031 {
14032 fNeedsSaving = true;
14033 break;
14034 }
14035 if (fNeedsSaving)
14036 {
14037 mData->mCurrentStateModified = TRUE;
14038 stsFlags |= SaveSTS_CurStateModified;
14039 }
14040 }
14041#endif
14042
14043 rc = saveStateSettings(stsFlags);
14044
14045 if ( ( oldMachineState != MachineState_PoweredOff
14046 && oldMachineState != MachineState_Aborted
14047 && oldMachineState != MachineState_Teleported
14048 )
14049 && ( aMachineState == MachineState_PoweredOff
14050 || aMachineState == MachineState_Aborted
14051 || aMachineState == MachineState_Teleported
14052 )
14053 )
14054 {
14055 /* we've been shut down for any reason */
14056 /* no special action so far */
14057 }
14058
14059 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14060 LogFlowThisFuncLeave();
14061 return rc;
14062}
14063
14064/**
14065 * Sends the current machine state value to the VM process.
14066 *
14067 * @note Locks this object for reading, then calls a client process.
14068 */
14069HRESULT SessionMachine::updateMachineStateOnClient()
14070{
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14073
14074 ComPtr<IInternalSessionControl> directControl;
14075 {
14076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14077 AssertReturn(!!mData, E_FAIL);
14078 directControl = mData->mSession.mDirectControl;
14079
14080 /* directControl may be already set to NULL here in #OnSessionEnd()
14081 * called too early by the direct session process while there is still
14082 * some operation (like deleting the snapshot) in progress. The client
14083 * process in this case is waiting inside Session::close() for the
14084 * "end session" process object to complete, while #uninit() called by
14085 * #checkForDeath() on the Watcher thread is waiting for the pending
14086 * operation to complete. For now, we accept this inconsistent behavior
14087 * and simply do nothing here. */
14088
14089 if (mData->mSession.mState == SessionState_Unlocking)
14090 return S_OK;
14091
14092 AssertReturn(!directControl.isNull(), E_FAIL);
14093 }
14094
14095 return directControl->UpdateMachineState(mData->mMachineState);
14096}
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