VirtualBox

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

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

Main/Metrics: Guests now push collected metrics to VBoxSVC instead of being polled (#6029)

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette