VirtualBox

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

Last change on this file since 36673 was 36673, checked in by vboxsync, 14 years ago

Main/Machine: add a way to terminate VM processes if they don't respond to the usual requests. Not an API change, just a comment change, and a slight VBoxManage touch up so that one can trigger it.

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

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