VirtualBox

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

Last change on this file since 40475 was 40432, checked in by vboxsync, 13 years ago

Main/VirtualBox+Machine+Medium: redesign the medium registry save logic, as the local lists in the threads caused inconsistencies with concurrent operations and the return of spurious errors

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