VirtualBox

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

Last change on this file since 40626 was 40538, checked in by vboxsync, 13 years ago

Main/Machine: lock order fixes

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