VirtualBox

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

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

GuestProperties/API: never send property change events with NULL strings.

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