VirtualBox

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

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

Main/Machine: fix error code check, Windows reports different code than Linux

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 452.0 KB
Line 
1/* $Id: MachineImpl.cpp 42213 2012-07-18 16:29:23Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPid = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mHWVirtExEnabled = true;
169 mHWVirtExNestedPagingEnabled = true;
170#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
171 mHWVirtExLargePagesEnabled = true;
172#else
173 /* Not supported on 32 bits hosts. */
174 mHWVirtExLargePagesEnabled = false;
175#endif
176 mHWVirtExVPIDEnabled = true;
177 mHWVirtExForceEnabled = false;
178#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
179 mHWVirtExExclusive = false;
180#else
181 mHWVirtExExclusive = true;
182#endif
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mSyntheticCpu = false;
189 mHpetEnabled = false;
190
191 /* default boot order: floppy - DVD - HDD */
192 mBootOrder[0] = DeviceType_Floppy;
193 mBootOrder[1] = DeviceType_DVD;
194 mBootOrder[2] = DeviceType_HardDisk;
195 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
196 mBootOrder[i] = DeviceType_Null;
197
198 mClipboardMode = ClipboardMode_Disabled;
199 mGuestPropertyNotificationPatterns = "";
200
201 mFirmwareType = FirmwareType_BIOS;
202 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
203 mPointingHidType = PointingHidType_PS2Mouse;
204 mChipsetType = ChipsetType_PIIX3;
205 mEmulatedUSBCardReaderEnabled = FALSE;
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 mSerialPorts(),
245 mParallelPorts(),
246 uRegistryNeedsSaving(0)
247{}
248
249Machine::~Machine()
250{}
251
252HRESULT Machine::FinalConstruct()
253{
254 LogFlowThisFunc(("\n"));
255 return BaseFinalConstruct();
256}
257
258void Machine::FinalRelease()
259{
260 LogFlowThisFunc(("\n"));
261 uninit();
262 BaseFinalRelease();
263}
264
265/**
266 * Initializes a new machine instance; this init() variant creates a new, empty machine.
267 * This gets called from VirtualBox::CreateMachine().
268 *
269 * @param aParent Associated parent object
270 * @param strConfigFile Local file system path to the VM settings file (can
271 * be relative to the VirtualBox config directory).
272 * @param strName name for the machine
273 * @param llGroups list of groups for the machine
274 * @param aOsType OS Type of this machine or NULL.
275 * @param aId UUID for the new machine.
276 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
277 *
278 * @return Success indicator. if not S_OK, the machine object is invalid
279 */
280HRESULT Machine::init(VirtualBox *aParent,
281 const Utf8Str &strConfigFile,
282 const Utf8Str &strName,
283 const StringsList &llGroups,
284 GuestOSType *aOsType,
285 const Guid &aId,
286 bool fForceOverwrite)
287{
288 LogFlowThisFuncEnter();
289 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
290
291 /* Enclose the state transition NotReady->InInit->Ready */
292 AutoInitSpan autoInitSpan(this);
293 AssertReturn(autoInitSpan.isOk(), E_FAIL);
294
295 HRESULT rc = initImpl(aParent, strConfigFile);
296 if (FAILED(rc)) return rc;
297
298 rc = tryCreateMachineConfigFile(fForceOverwrite);
299 if (FAILED(rc)) return rc;
300
301 if (SUCCEEDED(rc))
302 {
303 // create an empty machine config
304 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
305
306 rc = initDataAndChildObjects();
307 }
308
309 if (SUCCEEDED(rc))
310 {
311 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
312 mData->mAccessible = TRUE;
313
314 unconst(mData->mUuid) = aId;
315
316 mUserData->s.strName = strName;
317
318 mUserData->s.llGroups = llGroups;
319
320 // the "name sync" flag determines whether the machine directory gets renamed along
321 // with the machine file; say so if the settings file name is the same as the
322 // settings file parent directory (machine directory)
323 mUserData->s.fNameSync = isInOwnDir();
324
325 // initialize the default snapshots folder
326 rc = COMSETTER(SnapshotFolder)(NULL);
327 AssertComRC(rc);
328
329 if (aOsType)
330 {
331 /* Store OS type */
332 mUserData->s.strOsType = aOsType->id();
333
334 /* Apply BIOS defaults */
335 mBIOSSettings->applyDefaults(aOsType);
336
337 /* Apply network adapters defaults */
338 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
339 mNetworkAdapters[slot]->applyDefaults(aOsType);
340
341 /* Apply serial port defaults */
342 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
343 mSerialPorts[slot]->applyDefaults(aOsType);
344 }
345
346 /* At this point the changing of the current state modification
347 * flag is allowed. */
348 allowStateModification();
349
350 /* commit all changes made during the initialization */
351 commit();
352 }
353
354 /* Confirm a successful initialization when it's the case */
355 if (SUCCEEDED(rc))
356 {
357 if (mData->mAccessible)
358 autoInitSpan.setSucceeded();
359 else
360 autoInitSpan.setLimited();
361 }
362
363 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
364 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
365 mData->mRegistered,
366 mData->mAccessible,
367 rc));
368
369 LogFlowThisFuncLeave();
370
371 return rc;
372}
373
374/**
375 * Initializes a new instance with data from machine XML (formerly Init_Registered).
376 * Gets called in two modes:
377 *
378 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
379 * UUID is specified and we mark the machine as "registered";
380 *
381 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
382 * and the machine remains unregistered until RegisterMachine() is called.
383 *
384 * @param aParent Associated parent object
385 * @param aConfigFile Local file system path to the VM settings file (can
386 * be relative to the VirtualBox config directory).
387 * @param aId UUID of the machine or NULL (see above).
388 *
389 * @return Success indicator. if not S_OK, the machine object is invalid
390 */
391HRESULT Machine::init(VirtualBox *aParent,
392 const Utf8Str &strConfigFile,
393 const Guid *aId)
394{
395 LogFlowThisFuncEnter();
396 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
397
398 /* Enclose the state transition NotReady->InInit->Ready */
399 AutoInitSpan autoInitSpan(this);
400 AssertReturn(autoInitSpan.isOk(), E_FAIL);
401
402 HRESULT rc = initImpl(aParent, strConfigFile);
403 if (FAILED(rc)) return rc;
404
405 if (aId)
406 {
407 // loading a registered VM:
408 unconst(mData->mUuid) = *aId;
409 mData->mRegistered = TRUE;
410 // now load the settings from XML:
411 rc = registeredInit();
412 // this calls initDataAndChildObjects() and loadSettings()
413 }
414 else
415 {
416 // opening an unregistered VM (VirtualBox::OpenMachine()):
417 rc = initDataAndChildObjects();
418
419 if (SUCCEEDED(rc))
420 {
421 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
422 mData->mAccessible = TRUE;
423
424 try
425 {
426 // load and parse machine XML; this will throw on XML or logic errors
427 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
428
429 // reject VM UUID duplicates, they can happen if someone
430 // tries to register an already known VM config again
431 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
432 true /* fPermitInaccessible */,
433 false /* aDoSetError */,
434 NULL) != VBOX_E_OBJECT_NOT_FOUND)
435 {
436 throw setError(E_FAIL,
437 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
438 mData->m_strConfigFile.c_str());
439 }
440
441 // use UUID from machine config
442 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
443
444 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
445 NULL /* puuidRegistry */);
446 if (FAILED(rc)) throw rc;
447
448 /* At this point the changing of the current state modification
449 * flag is allowed. */
450 allowStateModification();
451
452 commit();
453 }
454 catch (HRESULT err)
455 {
456 /* we assume that error info is set by the thrower */
457 rc = err;
458 }
459 catch (...)
460 {
461 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
462 }
463 }
464 }
465
466 /* Confirm a successful initialization when it's the case */
467 if (SUCCEEDED(rc))
468 {
469 if (mData->mAccessible)
470 autoInitSpan.setSucceeded();
471 else
472 {
473 autoInitSpan.setLimited();
474
475 // uninit media from this machine's media registry, or else
476 // reloading the settings will fail
477 mParent->unregisterMachineMedia(getId());
478 }
479 }
480
481 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
482 "rc=%08X\n",
483 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
484 mData->mRegistered, mData->mAccessible, rc));
485
486 LogFlowThisFuncLeave();
487
488 return rc;
489}
490
491/**
492 * Initializes a new instance from a machine config that is already in memory
493 * (import OVF case). Since we are importing, the UUID in the machine
494 * config is ignored and we always generate a fresh one.
495 *
496 * @param strName Name for the new machine; this overrides what is specified in config and is used
497 * for the settings file as well.
498 * @param config Machine configuration loaded and parsed from XML.
499 *
500 * @return Success indicator. if not S_OK, the machine object is invalid
501 */
502HRESULT Machine::init(VirtualBox *aParent,
503 const Utf8Str &strName,
504 const settings::MachineConfigFile &config)
505{
506 LogFlowThisFuncEnter();
507
508 /* Enclose the state transition NotReady->InInit->Ready */
509 AutoInitSpan autoInitSpan(this);
510 AssertReturn(autoInitSpan.isOk(), E_FAIL);
511
512 Utf8Str strConfigFile;
513 aParent->getDefaultMachineFolder(strConfigFile);
514 strConfigFile.append(RTPATH_DELIMITER);
515 strConfigFile.append(strName);
516 strConfigFile.append(RTPATH_DELIMITER);
517 strConfigFile.append(strName);
518 strConfigFile.append(".vbox");
519
520 HRESULT rc = initImpl(aParent, strConfigFile);
521 if (FAILED(rc)) return rc;
522
523 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
524 if (FAILED(rc)) return rc;
525
526 rc = initDataAndChildObjects();
527
528 if (SUCCEEDED(rc))
529 {
530 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
531 mData->mAccessible = TRUE;
532
533 // create empty machine config for instance data
534 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
535
536 // generate fresh UUID, ignore machine config
537 unconst(mData->mUuid).create();
538
539 rc = loadMachineDataFromSettings(config,
540 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
541
542 // override VM name as well, it may be different
543 mUserData->s.strName = strName;
544
545 if (SUCCEEDED(rc))
546 {
547 /* At this point the changing of the current state modification
548 * flag is allowed. */
549 allowStateModification();
550
551 /* commit all changes made during the initialization */
552 commit();
553 }
554 }
555
556 /* Confirm a successful initialization when it's the case */
557 if (SUCCEEDED(rc))
558 {
559 if (mData->mAccessible)
560 autoInitSpan.setSucceeded();
561 else
562 {
563 autoInitSpan.setLimited();
564
565 // uninit media from this machine's media registry, or else
566 // reloading the settings will fail
567 mParent->unregisterMachineMedia(getId());
568 }
569 }
570
571 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
572 "rc=%08X\n",
573 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
574 mData->mRegistered, mData->mAccessible, rc));
575
576 LogFlowThisFuncLeave();
577
578 return rc;
579}
580
581/**
582 * Shared code between the various init() implementations.
583 * @param aParent
584 * @return
585 */
586HRESULT Machine::initImpl(VirtualBox *aParent,
587 const Utf8Str &strConfigFile)
588{
589 LogFlowThisFuncEnter();
590
591 AssertReturn(aParent, E_INVALIDARG);
592 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
593
594 HRESULT rc = S_OK;
595
596 /* share the parent weakly */
597 unconst(mParent) = aParent;
598
599 /* allocate the essential machine data structure (the rest will be
600 * allocated later by initDataAndChildObjects() */
601 mData.allocate();
602
603 /* memorize the config file name (as provided) */
604 mData->m_strConfigFile = strConfigFile;
605
606 /* get the full file name */
607 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
608 if (RT_FAILURE(vrc1))
609 return setError(VBOX_E_FILE_ERROR,
610 tr("Invalid machine settings file name '%s' (%Rrc)"),
611 strConfigFile.c_str(),
612 vrc1);
613
614 LogFlowThisFuncLeave();
615
616 return rc;
617}
618
619/**
620 * Tries to create a machine settings file in the path stored in the machine
621 * instance data. Used when a new machine is created to fail gracefully if
622 * the settings file could not be written (e.g. because machine dir is read-only).
623 * @return
624 */
625HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
626{
627 HRESULT rc = S_OK;
628
629 // when we create a new machine, we must be able to create the settings file
630 RTFILE f = NIL_RTFILE;
631 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
632 if ( RT_SUCCESS(vrc)
633 || vrc == VERR_SHARING_VIOLATION
634 )
635 {
636 if (RT_SUCCESS(vrc))
637 RTFileClose(f);
638 if (!fForceOverwrite)
639 rc = setError(VBOX_E_FILE_ERROR,
640 tr("Machine settings file '%s' already exists"),
641 mData->m_strConfigFileFull.c_str());
642 else
643 {
644 /* try to delete the config file, as otherwise the creation
645 * of a new settings file will fail. */
646 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
647 if (RT_FAILURE(vrc2))
648 rc = setError(VBOX_E_FILE_ERROR,
649 tr("Could not delete the existing settings file '%s' (%Rrc)"),
650 mData->m_strConfigFileFull.c_str(), vrc2);
651 }
652 }
653 else if ( vrc != VERR_FILE_NOT_FOUND
654 && vrc != VERR_PATH_NOT_FOUND
655 )
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Invalid machine settings file name '%s' (%Rrc)"),
658 mData->m_strConfigFileFull.c_str(),
659 vrc);
660 return rc;
661}
662
663/**
664 * Initializes the registered machine by loading the settings file.
665 * This method is separated from #init() in order to make it possible to
666 * retry the operation after VirtualBox startup instead of refusing to
667 * startup the whole VirtualBox server in case if the settings file of some
668 * registered VM is invalid or inaccessible.
669 *
670 * @note Must be always called from this object's write lock
671 * (unless called from #init() that doesn't need any locking).
672 * @note Locks the mUSBController method for writing.
673 * @note Subclasses must not call this method.
674 */
675HRESULT Machine::registeredInit()
676{
677 AssertReturn(!isSessionMachine(), E_FAIL);
678 AssertReturn(!isSnapshotMachine(), E_FAIL);
679 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
680 AssertReturn(!mData->mAccessible, E_FAIL);
681
682 HRESULT rc = initDataAndChildObjects();
683
684 if (SUCCEEDED(rc))
685 {
686 /* Temporarily reset the registered flag in order to let setters
687 * potentially called from loadSettings() succeed (isMutable() used in
688 * all setters will return FALSE for a Machine instance if mRegistered
689 * is TRUE). */
690 mData->mRegistered = FALSE;
691
692 try
693 {
694 // load and parse machine XML; this will throw on XML or logic errors
695 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
696
697 if (mData->mUuid != mData->pMachineConfigFile->uuid)
698 throw setError(E_FAIL,
699 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
700 mData->pMachineConfigFile->uuid.raw(),
701 mData->m_strConfigFileFull.c_str(),
702 mData->mUuid.toString().c_str(),
703 mParent->settingsFilePath().c_str());
704
705 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
706 NULL /* const Guid *puuidRegistry */);
707 if (FAILED(rc)) throw rc;
708 }
709 catch (HRESULT err)
710 {
711 /* we assume that error info is set by the thrower */
712 rc = err;
713 }
714 catch (...)
715 {
716 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
717 }
718
719 /* Restore the registered flag (even on failure) */
720 mData->mRegistered = TRUE;
721 }
722
723 if (SUCCEEDED(rc))
724 {
725 /* Set mAccessible to TRUE only if we successfully locked and loaded
726 * the settings file */
727 mData->mAccessible = TRUE;
728
729 /* commit all changes made during loading the settings file */
730 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
731 /// @todo r=klaus for some reason the settings loading logic backs up
732 // the settings, and therefore a commit is needed. Should probably be changed.
733 }
734 else
735 {
736 /* If the machine is registered, then, instead of returning a
737 * failure, we mark it as inaccessible and set the result to
738 * success to give it a try later */
739
740 /* fetch the current error info */
741 mData->mAccessError = com::ErrorInfo();
742 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
743 mData->mUuid.raw(),
744 mData->mAccessError.getText().raw()));
745
746 /* rollback all changes */
747 rollback(false /* aNotify */);
748
749 // uninit media from this machine's media registry, or else
750 // reloading the settings will fail
751 mParent->unregisterMachineMedia(getId());
752
753 /* uninitialize the common part to make sure all data is reset to
754 * default (null) values */
755 uninitDataAndChildObjects();
756
757 rc = S_OK;
758 }
759
760 return rc;
761}
762
763/**
764 * Uninitializes the instance.
765 * Called either from FinalRelease() or by the parent when it gets destroyed.
766 *
767 * @note The caller of this method must make sure that this object
768 * a) doesn't have active callers on the current thread and b) is not locked
769 * by the current thread; otherwise uninit() will hang either a) due to
770 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
771 * a dead-lock caused by this thread waiting for all callers on the other
772 * threads are done but preventing them from doing so by holding a lock.
773 */
774void Machine::uninit()
775{
776 LogFlowThisFuncEnter();
777
778 Assert(!isWriteLockOnCurrentThread());
779
780 Assert(!uRegistryNeedsSaving);
781 if (uRegistryNeedsSaving)
782 {
783 AutoCaller autoCaller(this);
784 if (SUCCEEDED(autoCaller.rc()))
785 {
786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
787 saveSettings(NULL, Machine::SaveS_Force);
788 }
789 }
790
791 /* Enclose the state transition Ready->InUninit->NotReady */
792 AutoUninitSpan autoUninitSpan(this);
793 if (autoUninitSpan.uninitDone())
794 return;
795
796 Assert(!isSnapshotMachine());
797 Assert(!isSessionMachine());
798 Assert(!!mData);
799
800 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
801 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
802
803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
804
805 if (!mData->mSession.mMachine.isNull())
806 {
807 /* Theoretically, this can only happen if the VirtualBox server has been
808 * terminated while there were clients running that owned open direct
809 * sessions. Since in this case we are definitely called by
810 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
811 * won't happen on the client watcher thread (because it does
812 * VirtualBox::addCaller() for the duration of the
813 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
814 * cannot happen until the VirtualBox caller is released). This is
815 * important, because SessionMachine::uninit() cannot correctly operate
816 * after we return from this method (it expects the Machine instance is
817 * still valid). We'll call it ourselves below.
818 */
819 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
820 (SessionMachine*)mData->mSession.mMachine));
821
822 if (Global::IsOnlineOrTransient(mData->mMachineState))
823 {
824 LogWarningThisFunc(("Setting state to Aborted!\n"));
825 /* set machine state using SessionMachine reimplementation */
826 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
827 }
828
829 /*
830 * Uninitialize SessionMachine using public uninit() to indicate
831 * an unexpected uninitialization.
832 */
833 mData->mSession.mMachine->uninit();
834 /* SessionMachine::uninit() must set mSession.mMachine to null */
835 Assert(mData->mSession.mMachine.isNull());
836 }
837
838 // uninit media from this machine's media registry, if they're still there
839 Guid uuidMachine(getId());
840
841 /* XXX This will fail with
842 * "cannot be closed because it is still attached to 1 virtual machines"
843 * because at this point we did not call uninitDataAndChildObjects() yet
844 * and therefore also removeBackReference() for all these mediums was not called! */
845 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
846 mParent->unregisterMachineMedia(uuidMachine);
847
848 /* the lock is no more necessary (SessionMachine is uninitialized) */
849 alock.release();
850
851 // has machine been modified?
852 if (mData->flModifications)
853 {
854 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
855 rollback(false /* aNotify */);
856 }
857
858 if (mData->mAccessible)
859 uninitDataAndChildObjects();
860
861 /* free the essential data structure last */
862 mData.free();
863
864 LogFlowThisFuncLeave();
865}
866
867// IMachine properties
868/////////////////////////////////////////////////////////////////////////////
869
870STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
871{
872 CheckComArgOutPointerValid(aParent);
873
874 AutoLimitedCaller autoCaller(this);
875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
876
877 /* mParent is constant during life time, no need to lock */
878 ComObjPtr<VirtualBox> pVirtualBox(mParent);
879 pVirtualBox.queryInterfaceTo(aParent);
880
881 return S_OK;
882}
883
884STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
885{
886 CheckComArgOutPointerValid(aAccessible);
887
888 AutoLimitedCaller autoCaller(this);
889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
890
891 LogFlowThisFunc(("ENTER\n"));
892
893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
894
895 HRESULT rc = S_OK;
896
897 if (!mData->mAccessible)
898 {
899 /* try to initialize the VM once more if not accessible */
900
901 AutoReinitSpan autoReinitSpan(this);
902 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
903
904#ifdef DEBUG
905 LogFlowThisFunc(("Dumping media backreferences\n"));
906 mParent->dumpAllBackRefs();
907#endif
908
909 if (mData->pMachineConfigFile)
910 {
911 // reset the XML file to force loadSettings() (called from registeredInit())
912 // to parse it again; the file might have changed
913 delete mData->pMachineConfigFile;
914 mData->pMachineConfigFile = NULL;
915 }
916
917 rc = registeredInit();
918
919 if (SUCCEEDED(rc) && mData->mAccessible)
920 {
921 autoReinitSpan.setSucceeded();
922
923 /* make sure interesting parties will notice the accessibility
924 * state change */
925 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
926 mParent->onMachineDataChange(mData->mUuid);
927 }
928 }
929
930 if (SUCCEEDED(rc))
931 *aAccessible = mData->mAccessible;
932
933 LogFlowThisFuncLeave();
934
935 return rc;
936}
937
938STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
939{
940 CheckComArgOutPointerValid(aAccessError);
941
942 AutoLimitedCaller autoCaller(this);
943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
944
945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
946
947 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
948 {
949 /* return shortly */
950 aAccessError = NULL;
951 return S_OK;
952 }
953
954 HRESULT rc = S_OK;
955
956 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
957 rc = errorInfo.createObject();
958 if (SUCCEEDED(rc))
959 {
960 errorInfo->init(mData->mAccessError.getResultCode(),
961 mData->mAccessError.getInterfaceID().ref(),
962 Utf8Str(mData->mAccessError.getComponent()).c_str(),
963 Utf8Str(mData->mAccessError.getText()));
964 rc = errorInfo.queryInterfaceTo(aAccessError);
965 }
966
967 return rc;
968}
969
970STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
971{
972 CheckComArgOutPointerValid(aName);
973
974 AutoCaller autoCaller(this);
975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
976
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 mUserData->s.strName.cloneTo(aName);
980
981 return S_OK;
982}
983
984STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
985{
986 CheckComArgStrNotEmptyOrNull(aName);
987
988 AutoCaller autoCaller(this);
989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
990
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994 if (test.isNotEmpty())
995 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
996
997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 HRESULT rc = checkStateDependency(MutableStateDep);
1000 if (FAILED(rc)) return rc;
1001
1002 setModified(IsModified_MachineData);
1003 mUserData.backup();
1004 mUserData->s.strName = aName;
1005
1006 return S_OK;
1007}
1008
1009STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1010{
1011 CheckComArgOutPointerValid(aDescription);
1012
1013 AutoCaller autoCaller(this);
1014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1015
1016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 mUserData->s.strDescription.cloneTo(aDescription);
1019
1020 return S_OK;
1021}
1022
1023STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1024{
1025 AutoCaller autoCaller(this);
1026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1027
1028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1029
1030 HRESULT rc = checkStateDependency(MutableStateDep);
1031 if (FAILED(rc)) return rc;
1032
1033 setModified(IsModified_MachineData);
1034 mUserData.backup();
1035 mUserData->s.strDescription = aDescription;
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1041{
1042 CheckComArgOutPointerValid(aId);
1043
1044 AutoLimitedCaller autoCaller(this);
1045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1046
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048
1049 mData->mUuid.toUtf16().cloneTo(aId);
1050
1051 return S_OK;
1052}
1053
1054STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1055{
1056 CheckComArgOutSafeArrayPointerValid(aGroups);
1057
1058 AutoCaller autoCaller(this);
1059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1060
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1063 size_t i = 0;
1064 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1065 it != mUserData->s.llGroups.end();
1066 ++it, i++)
1067 {
1068 Bstr tmp = *it;
1069 tmp.cloneTo(&groups[i]);
1070 }
1071 groups.detachTo(ComSafeArrayOutArg(aGroups));
1072
1073 return S_OK;
1074}
1075
1076STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1077{
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 StringsList llGroups;
1082 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1083 if (FAILED(rc))
1084 return rc;
1085
1086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1087
1088 rc = checkStateDependency(MutableStateDep);
1089 if (FAILED(rc)) return rc;
1090
1091 setModified(IsModified_MachineData);
1092 mUserData.backup();
1093 mUserData->s.llGroups = llGroups;
1094
1095 return S_OK;
1096}
1097
1098STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1099{
1100 CheckComArgOutPointerValid(aOSTypeId);
1101
1102 AutoCaller autoCaller(this);
1103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1104
1105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 mUserData->s.strOsType.cloneTo(aOSTypeId);
1108
1109 return S_OK;
1110}
1111
1112STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1113{
1114 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1115
1116 AutoCaller autoCaller(this);
1117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1118
1119 /* look up the object by Id to check it is valid */
1120 ComPtr<IGuestOSType> guestOSType;
1121 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1122 if (FAILED(rc)) return rc;
1123
1124 /* when setting, always use the "etalon" value for consistency -- lookup
1125 * by ID is case-insensitive and the input value may have different case */
1126 Bstr osTypeId;
1127 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1128 if (FAILED(rc)) return rc;
1129
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 rc = checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 setModified(IsModified_MachineData);
1136 mUserData.backup();
1137 mUserData->s.strOsType = osTypeId;
1138
1139 return S_OK;
1140}
1141
1142
1143STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1144{
1145 CheckComArgOutPointerValid(aFirmwareType);
1146
1147 AutoCaller autoCaller(this);
1148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1149
1150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1151
1152 *aFirmwareType = mHWData->mFirmwareType;
1153
1154 return S_OK;
1155}
1156
1157STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1158{
1159 AutoCaller autoCaller(this);
1160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 HRESULT rc = checkStateDependency(MutableStateDep);
1164 if (FAILED(rc)) return rc;
1165
1166 setModified(IsModified_MachineData);
1167 mHWData.backup();
1168 mHWData->mFirmwareType = aFirmwareType;
1169
1170 return S_OK;
1171}
1172
1173STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1174{
1175 CheckComArgOutPointerValid(aKeyboardHidType);
1176
1177 AutoCaller autoCaller(this);
1178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1179
1180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 *aKeyboardHidType = mHWData->mKeyboardHidType;
1183
1184 return S_OK;
1185}
1186
1187STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1188{
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mKeyboardHidType = aKeyboardHidType;
1199
1200 return S_OK;
1201}
1202
1203STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1204{
1205 CheckComArgOutPointerValid(aPointingHidType);
1206
1207 AutoCaller autoCaller(this);
1208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1209
1210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1211
1212 *aPointingHidType = mHWData->mPointingHidType;
1213
1214 return S_OK;
1215}
1216
1217STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1218{
1219 AutoCaller autoCaller(this);
1220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 HRESULT rc = checkStateDependency(MutableStateDep);
1224 if (FAILED(rc)) return rc;
1225
1226 setModified(IsModified_MachineData);
1227 mHWData.backup();
1228 mHWData->mPointingHidType = aPointingHidType;
1229
1230 return S_OK;
1231}
1232
1233STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1234{
1235 CheckComArgOutPointerValid(aChipsetType);
1236
1237 AutoCaller autoCaller(this);
1238 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1239
1240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1241
1242 *aChipsetType = mHWData->mChipsetType;
1243
1244 return S_OK;
1245}
1246
1247STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1248{
1249 AutoCaller autoCaller(this);
1250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1252
1253 HRESULT rc = checkStateDependency(MutableStateDep);
1254 if (FAILED(rc)) return rc;
1255
1256 if (aChipsetType != mHWData->mChipsetType)
1257 {
1258 setModified(IsModified_MachineData);
1259 mHWData.backup();
1260 mHWData->mChipsetType = aChipsetType;
1261
1262 // Resize network adapter array, to be finalized on commit/rollback.
1263 // We must not throw away entries yet, otherwise settings are lost
1264 // without a way to roll back.
1265 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1266 uint32_t oldCount = mNetworkAdapters.size();
1267 if (newCount > oldCount)
1268 {
1269 mNetworkAdapters.resize(newCount);
1270 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1271 {
1272 unconst(mNetworkAdapters[slot]).createObject();
1273 mNetworkAdapters[slot]->init(this, slot);
1274 }
1275 }
1276 }
1277
1278 return S_OK;
1279}
1280
1281STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1282{
1283 CheckComArgOutPointerValid(aHWVersion);
1284
1285 AutoCaller autoCaller(this);
1286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1287
1288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1289
1290 mHWData->mHWVersion.cloneTo(aHWVersion);
1291
1292 return S_OK;
1293}
1294
1295STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1296{
1297 /* check known version */
1298 Utf8Str hwVersion = aHWVersion;
1299 if ( hwVersion.compare("1") != 0
1300 && hwVersion.compare("2") != 0)
1301 return setError(E_INVALIDARG,
1302 tr("Invalid hardware version: %ls\n"), aHWVersion);
1303
1304 AutoCaller autoCaller(this);
1305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1306
1307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 HRESULT rc = checkStateDependency(MutableStateDep);
1310 if (FAILED(rc)) return rc;
1311
1312 setModified(IsModified_MachineData);
1313 mHWData.backup();
1314 mHWData->mHWVersion = hwVersion;
1315
1316 return S_OK;
1317}
1318
1319STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1320{
1321 CheckComArgOutPointerValid(aUUID);
1322
1323 AutoCaller autoCaller(this);
1324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1325
1326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1327
1328 if (!mHWData->mHardwareUUID.isEmpty())
1329 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1330 else
1331 mData->mUuid.toUtf16().cloneTo(aUUID);
1332
1333 return S_OK;
1334}
1335
1336STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1337{
1338 Guid hardwareUUID(aUUID);
1339 if (hardwareUUID.isEmpty())
1340 return E_INVALIDARG;
1341
1342 AutoCaller autoCaller(this);
1343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1344
1345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 HRESULT rc = checkStateDependency(MutableStateDep);
1348 if (FAILED(rc)) return rc;
1349
1350 setModified(IsModified_MachineData);
1351 mHWData.backup();
1352 if (hardwareUUID == mData->mUuid)
1353 mHWData->mHardwareUUID.clear();
1354 else
1355 mHWData->mHardwareUUID = hardwareUUID;
1356
1357 return S_OK;
1358}
1359
1360STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1361{
1362 CheckComArgOutPointerValid(memorySize);
1363
1364 AutoCaller autoCaller(this);
1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1366
1367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 *memorySize = mHWData->mMemorySize;
1370
1371 return S_OK;
1372}
1373
1374STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1375{
1376 /* check RAM limits */
1377 if ( memorySize < MM_RAM_MIN_IN_MB
1378 || memorySize > MM_RAM_MAX_IN_MB
1379 )
1380 return setError(E_INVALIDARG,
1381 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1382 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1383
1384 AutoCaller autoCaller(this);
1385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1386
1387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1388
1389 HRESULT rc = checkStateDependency(MutableStateDep);
1390 if (FAILED(rc)) return rc;
1391
1392 setModified(IsModified_MachineData);
1393 mHWData.backup();
1394 mHWData->mMemorySize = memorySize;
1395
1396 return S_OK;
1397}
1398
1399STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1400{
1401 CheckComArgOutPointerValid(CPUCount);
1402
1403 AutoCaller autoCaller(this);
1404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1405
1406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 *CPUCount = mHWData->mCPUCount;
1409
1410 return S_OK;
1411}
1412
1413STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1414{
1415 /* check CPU limits */
1416 if ( CPUCount < SchemaDefs::MinCPUCount
1417 || CPUCount > SchemaDefs::MaxCPUCount
1418 )
1419 return setError(E_INVALIDARG,
1420 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1421 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1422
1423 AutoCaller autoCaller(this);
1424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1425
1426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1429 if (mHWData->mCPUHotPlugEnabled)
1430 {
1431 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1432 {
1433 if (mHWData->mCPUAttached[idx])
1434 return setError(E_INVALIDARG,
1435 tr("There is still a CPU attached to socket %lu."
1436 "Detach the CPU before removing the socket"),
1437 CPUCount, idx+1);
1438 }
1439 }
1440
1441 HRESULT rc = checkStateDependency(MutableStateDep);
1442 if (FAILED(rc)) return rc;
1443
1444 setModified(IsModified_MachineData);
1445 mHWData.backup();
1446 mHWData->mCPUCount = CPUCount;
1447
1448 return S_OK;
1449}
1450
1451STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1452{
1453 CheckComArgOutPointerValid(aExecutionCap);
1454
1455 AutoCaller autoCaller(this);
1456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1457
1458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 *aExecutionCap = mHWData->mCpuExecutionCap;
1461
1462 return S_OK;
1463}
1464
1465STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1466{
1467 HRESULT rc = S_OK;
1468
1469 /* check throttle limits */
1470 if ( aExecutionCap < 1
1471 || aExecutionCap > 100
1472 )
1473 return setError(E_INVALIDARG,
1474 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1475 aExecutionCap, 1, 100);
1476
1477 AutoCaller autoCaller(this);
1478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1479
1480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 alock.release();
1483 rc = onCPUExecutionCapChange(aExecutionCap);
1484 alock.acquire();
1485 if (FAILED(rc)) return rc;
1486
1487 setModified(IsModified_MachineData);
1488 mHWData.backup();
1489 mHWData->mCpuExecutionCap = aExecutionCap;
1490
1491 /* Save settings if online - todo why is this required?? */
1492 if (Global::IsOnline(mData->mMachineState))
1493 saveSettings(NULL);
1494
1495 return S_OK;
1496}
1497
1498
1499STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1500{
1501 CheckComArgOutPointerValid(enabled);
1502
1503 AutoCaller autoCaller(this);
1504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1505
1506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 *enabled = mHWData->mCPUHotPlugEnabled;
1509
1510 return S_OK;
1511}
1512
1513STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1514{
1515 HRESULT rc = S_OK;
1516
1517 AutoCaller autoCaller(this);
1518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1519
1520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1521
1522 rc = checkStateDependency(MutableStateDep);
1523 if (FAILED(rc)) return rc;
1524
1525 if (mHWData->mCPUHotPlugEnabled != enabled)
1526 {
1527 if (enabled)
1528 {
1529 setModified(IsModified_MachineData);
1530 mHWData.backup();
1531
1532 /* Add the amount of CPUs currently attached */
1533 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1534 {
1535 mHWData->mCPUAttached[i] = true;
1536 }
1537 }
1538 else
1539 {
1540 /*
1541 * We can disable hotplug only if the amount of maximum CPUs is equal
1542 * to the amount of attached CPUs
1543 */
1544 unsigned cCpusAttached = 0;
1545 unsigned iHighestId = 0;
1546
1547 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1548 {
1549 if (mHWData->mCPUAttached[i])
1550 {
1551 cCpusAttached++;
1552 iHighestId = i;
1553 }
1554 }
1555
1556 if ( (cCpusAttached != mHWData->mCPUCount)
1557 || (iHighestId >= mHWData->mCPUCount))
1558 return setError(E_INVALIDARG,
1559 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1560
1561 setModified(IsModified_MachineData);
1562 mHWData.backup();
1563 }
1564 }
1565
1566 mHWData->mCPUHotPlugEnabled = enabled;
1567
1568 return rc;
1569}
1570
1571STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1572{
1573#ifdef VBOX_WITH_USB_CARDREADER
1574 CheckComArgOutPointerValid(enabled);
1575
1576 AutoCaller autoCaller(this);
1577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1578
1579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1582
1583 return S_OK;
1584#else
1585 NOREF(enabled);
1586 return E_NOTIMPL;
1587#endif
1588}
1589
1590STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1591{
1592#ifdef VBOX_WITH_USB_CARDREADER
1593 AutoCaller autoCaller(this);
1594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
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->mEmulatedUSBCardReaderEnabled = enabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(enabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1612{
1613 NOREF(enabled);
1614 return E_NOTIMPL;
1615}
1616
1617STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1618{
1619 NOREF(enabled);
1620 return E_NOTIMPL;
1621}
1622
1623STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1624{
1625 CheckComArgOutPointerValid(enabled);
1626
1627 AutoCaller autoCaller(this);
1628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 *enabled = mHWData->mHpetEnabled;
1632
1633 return S_OK;
1634}
1635
1636STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1637{
1638 HRESULT rc = S_OK;
1639
1640 AutoCaller autoCaller(this);
1641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1643
1644 rc = checkStateDependency(MutableStateDep);
1645 if (FAILED(rc)) return rc;
1646
1647 setModified(IsModified_MachineData);
1648 mHWData.backup();
1649
1650 mHWData->mHpetEnabled = enabled;
1651
1652 return rc;
1653}
1654
1655STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1656{
1657 CheckComArgOutPointerValid(memorySize);
1658
1659 AutoCaller autoCaller(this);
1660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1661
1662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 *memorySize = mHWData->mVRAMSize;
1665
1666 return S_OK;
1667}
1668
1669STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1670{
1671 /* check VRAM limits */
1672 if (memorySize < SchemaDefs::MinGuestVRAM ||
1673 memorySize > SchemaDefs::MaxGuestVRAM)
1674 return setError(E_INVALIDARG,
1675 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1676 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1677
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 HRESULT rc = checkStateDependency(MutableStateDep);
1684 if (FAILED(rc)) return rc;
1685
1686 setModified(IsModified_MachineData);
1687 mHWData.backup();
1688 mHWData->mVRAMSize = memorySize;
1689
1690 return S_OK;
1691}
1692
1693/** @todo this method should not be public */
1694STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1695{
1696 CheckComArgOutPointerValid(memoryBalloonSize);
1697
1698 AutoCaller autoCaller(this);
1699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1700
1701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1704
1705 return S_OK;
1706}
1707
1708/**
1709 * Set the memory balloon size.
1710 *
1711 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1712 * we have to make sure that we never call IGuest from here.
1713 */
1714STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1715{
1716 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1717#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1718 /* check limits */
1719 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1720 return setError(E_INVALIDARG,
1721 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1722 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1723
1724 AutoCaller autoCaller(this);
1725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1726
1727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1728
1729 setModified(IsModified_MachineData);
1730 mHWData.backup();
1731 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1732
1733 return S_OK;
1734#else
1735 NOREF(memoryBalloonSize);
1736 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1737#endif
1738}
1739
1740STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1741{
1742 CheckComArgOutPointerValid(enabled);
1743
1744 AutoCaller autoCaller(this);
1745 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1746
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *enabled = mHWData->mPageFusionEnabled;
1750 return S_OK;
1751}
1752
1753STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1754{
1755#ifdef VBOX_WITH_PAGE_SHARING
1756 AutoCaller autoCaller(this);
1757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1758
1759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1762 setModified(IsModified_MachineData);
1763 mHWData.backup();
1764 mHWData->mPageFusionEnabled = enabled;
1765 return S_OK;
1766#else
1767 NOREF(enabled);
1768 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1769#endif
1770}
1771
1772STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1773{
1774 CheckComArgOutPointerValid(enabled);
1775
1776 AutoCaller autoCaller(this);
1777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1778
1779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1780
1781 *enabled = mHWData->mAccelerate3DEnabled;
1782
1783 return S_OK;
1784}
1785
1786STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1787{
1788 AutoCaller autoCaller(this);
1789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1790
1791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 HRESULT rc = checkStateDependency(MutableStateDep);
1794 if (FAILED(rc)) return rc;
1795
1796 /** @todo check validity! */
1797
1798 setModified(IsModified_MachineData);
1799 mHWData.backup();
1800 mHWData->mAccelerate3DEnabled = enable;
1801
1802 return S_OK;
1803}
1804
1805
1806STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1807{
1808 CheckComArgOutPointerValid(enabled);
1809
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 *enabled = mHWData->mAccelerate2DVideoEnabled;
1816
1817 return S_OK;
1818}
1819
1820STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1821{
1822 AutoCaller autoCaller(this);
1823 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1824
1825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 HRESULT rc = checkStateDependency(MutableStateDep);
1828 if (FAILED(rc)) return rc;
1829
1830 /** @todo check validity! */
1831
1832 setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mAccelerate2DVideoEnabled = enable;
1835
1836 return S_OK;
1837}
1838
1839STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1840{
1841 CheckComArgOutPointerValid(monitorCount);
1842
1843 AutoCaller autoCaller(this);
1844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1845
1846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 *monitorCount = mHWData->mMonitorCount;
1849
1850 return S_OK;
1851}
1852
1853STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1854{
1855 /* make sure monitor count is a sensible number */
1856 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1857 return setError(E_INVALIDARG,
1858 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1859 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1860
1861 AutoCaller autoCaller(this);
1862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1863
1864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 HRESULT rc = checkStateDependency(MutableStateDep);
1867 if (FAILED(rc)) return rc;
1868
1869 setModified(IsModified_MachineData);
1870 mHWData.backup();
1871 mHWData->mMonitorCount = monitorCount;
1872
1873 return S_OK;
1874}
1875
1876STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1877{
1878 CheckComArgOutPointerValid(biosSettings);
1879
1880 AutoCaller autoCaller(this);
1881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1882
1883 /* mBIOSSettings is constant during life time, no need to lock */
1884 mBIOSSettings.queryInterfaceTo(biosSettings);
1885
1886 return S_OK;
1887}
1888
1889STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1890{
1891 CheckComArgOutPointerValid(aVal);
1892
1893 AutoCaller autoCaller(this);
1894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1895
1896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1897
1898 switch(property)
1899 {
1900 case CPUPropertyType_PAE:
1901 *aVal = mHWData->mPAEEnabled;
1902 break;
1903
1904 case CPUPropertyType_Synthetic:
1905 *aVal = mHWData->mSyntheticCpu;
1906 break;
1907
1908 default:
1909 return E_INVALIDARG;
1910 }
1911 return S_OK;
1912}
1913
1914STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1915{
1916 AutoCaller autoCaller(this);
1917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1918
1919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 HRESULT rc = checkStateDependency(MutableStateDep);
1922 if (FAILED(rc)) return rc;
1923
1924 switch(property)
1925 {
1926 case CPUPropertyType_PAE:
1927 setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 mHWData->mPAEEnabled = !!aVal;
1930 break;
1931
1932 case CPUPropertyType_Synthetic:
1933 setModified(IsModified_MachineData);
1934 mHWData.backup();
1935 mHWData->mSyntheticCpu = !!aVal;
1936 break;
1937
1938 default:
1939 return E_INVALIDARG;
1940 }
1941 return S_OK;
1942}
1943
1944STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1945{
1946 CheckComArgOutPointerValid(aValEax);
1947 CheckComArgOutPointerValid(aValEbx);
1948 CheckComArgOutPointerValid(aValEcx);
1949 CheckComArgOutPointerValid(aValEdx);
1950
1951 AutoCaller autoCaller(this);
1952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1953
1954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1955
1956 switch(aId)
1957 {
1958 case 0x0:
1959 case 0x1:
1960 case 0x2:
1961 case 0x3:
1962 case 0x4:
1963 case 0x5:
1964 case 0x6:
1965 case 0x7:
1966 case 0x8:
1967 case 0x9:
1968 case 0xA:
1969 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1970 return E_INVALIDARG;
1971
1972 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1973 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1974 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1975 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1976 break;
1977
1978 case 0x80000000:
1979 case 0x80000001:
1980 case 0x80000002:
1981 case 0x80000003:
1982 case 0x80000004:
1983 case 0x80000005:
1984 case 0x80000006:
1985 case 0x80000007:
1986 case 0x80000008:
1987 case 0x80000009:
1988 case 0x8000000A:
1989 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1990 return E_INVALIDARG;
1991
1992 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1993 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1994 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1995 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1996 break;
1997
1998 default:
1999 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2000 }
2001 return S_OK;
2002}
2003
2004STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2005{
2006 AutoCaller autoCaller(this);
2007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 HRESULT rc = checkStateDependency(MutableStateDep);
2012 if (FAILED(rc)) return rc;
2013
2014 switch(aId)
2015 {
2016 case 0x0:
2017 case 0x1:
2018 case 0x2:
2019 case 0x3:
2020 case 0x4:
2021 case 0x5:
2022 case 0x6:
2023 case 0x7:
2024 case 0x8:
2025 case 0x9:
2026 case 0xA:
2027 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2028 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2029 setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2032 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2033 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2034 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2035 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2036 break;
2037
2038 case 0x80000000:
2039 case 0x80000001:
2040 case 0x80000002:
2041 case 0x80000003:
2042 case 0x80000004:
2043 case 0x80000005:
2044 case 0x80000006:
2045 case 0x80000007:
2046 case 0x80000008:
2047 case 0x80000009:
2048 case 0x8000000A:
2049 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2050 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2051 setModified(IsModified_MachineData);
2052 mHWData.backup();
2053 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2054 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2055 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2056 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2057 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2058 break;
2059
2060 default:
2061 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2062 }
2063 return S_OK;
2064}
2065
2066STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2067{
2068 AutoCaller autoCaller(this);
2069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2070
2071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2072
2073 HRESULT rc = checkStateDependency(MutableStateDep);
2074 if (FAILED(rc)) return rc;
2075
2076 switch(aId)
2077 {
2078 case 0x0:
2079 case 0x1:
2080 case 0x2:
2081 case 0x3:
2082 case 0x4:
2083 case 0x5:
2084 case 0x6:
2085 case 0x7:
2086 case 0x8:
2087 case 0x9:
2088 case 0xA:
2089 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2090 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2091 setModified(IsModified_MachineData);
2092 mHWData.backup();
2093 /* Invalidate leaf. */
2094 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2095 break;
2096
2097 case 0x80000000:
2098 case 0x80000001:
2099 case 0x80000002:
2100 case 0x80000003:
2101 case 0x80000004:
2102 case 0x80000005:
2103 case 0x80000006:
2104 case 0x80000007:
2105 case 0x80000008:
2106 case 0x80000009:
2107 case 0x8000000A:
2108 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2109 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2110 setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 /* Invalidate leaf. */
2113 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2114 break;
2115
2116 default:
2117 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2118 }
2119 return S_OK;
2120}
2121
2122STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2123{
2124 AutoCaller autoCaller(this);
2125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2126
2127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 HRESULT rc = checkStateDependency(MutableStateDep);
2130 if (FAILED(rc)) return rc;
2131
2132 setModified(IsModified_MachineData);
2133 mHWData.backup();
2134
2135 /* Invalidate all standard leafs. */
2136 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2137 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2138
2139 /* Invalidate all extended leafs. */
2140 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2141 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2142
2143 return S_OK;
2144}
2145
2146STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2147{
2148 CheckComArgOutPointerValid(aVal);
2149
2150 AutoCaller autoCaller(this);
2151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2152
2153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 switch(property)
2156 {
2157 case HWVirtExPropertyType_Enabled:
2158 *aVal = mHWData->mHWVirtExEnabled;
2159 break;
2160
2161 case HWVirtExPropertyType_Exclusive:
2162 *aVal = mHWData->mHWVirtExExclusive;
2163 break;
2164
2165 case HWVirtExPropertyType_VPID:
2166 *aVal = mHWData->mHWVirtExVPIDEnabled;
2167 break;
2168
2169 case HWVirtExPropertyType_NestedPaging:
2170 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2171 break;
2172
2173 case HWVirtExPropertyType_LargePages:
2174 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2175#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2176 *aVal = FALSE;
2177#endif
2178 break;
2179
2180 case HWVirtExPropertyType_Force:
2181 *aVal = mHWData->mHWVirtExForceEnabled;
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2191{
2192 AutoCaller autoCaller(this);
2193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2194
2195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 HRESULT rc = checkStateDependency(MutableStateDep);
2198 if (FAILED(rc)) return rc;
2199
2200 switch(property)
2201 {
2202 case HWVirtExPropertyType_Enabled:
2203 setModified(IsModified_MachineData);
2204 mHWData.backup();
2205 mHWData->mHWVirtExEnabled = !!aVal;
2206 break;
2207
2208 case HWVirtExPropertyType_Exclusive:
2209 setModified(IsModified_MachineData);
2210 mHWData.backup();
2211 mHWData->mHWVirtExExclusive = !!aVal;
2212 break;
2213
2214 case HWVirtExPropertyType_VPID:
2215 setModified(IsModified_MachineData);
2216 mHWData.backup();
2217 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2218 break;
2219
2220 case HWVirtExPropertyType_NestedPaging:
2221 setModified(IsModified_MachineData);
2222 mHWData.backup();
2223 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2224 break;
2225
2226 case HWVirtExPropertyType_LargePages:
2227 setModified(IsModified_MachineData);
2228 mHWData.backup();
2229 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2230 break;
2231
2232 case HWVirtExPropertyType_Force:
2233 setModified(IsModified_MachineData);
2234 mHWData.backup();
2235 mHWData->mHWVirtExForceEnabled = !!aVal;
2236 break;
2237
2238 default:
2239 return E_INVALIDARG;
2240 }
2241
2242 return S_OK;
2243}
2244
2245STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2246{
2247 CheckComArgOutPointerValid(aSnapshotFolder);
2248
2249 AutoCaller autoCaller(this);
2250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2251
2252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 Utf8Str strFullSnapshotFolder;
2255 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2256 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2257
2258 return S_OK;
2259}
2260
2261STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2262{
2263 /* @todo (r=dmik):
2264 * 1. Allow to change the name of the snapshot folder containing snapshots
2265 * 2. Rename the folder on disk instead of just changing the property
2266 * value (to be smart and not to leave garbage). Note that it cannot be
2267 * done here because the change may be rolled back. Thus, the right
2268 * place is #saveSettings().
2269 */
2270
2271 AutoCaller autoCaller(this);
2272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2273
2274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2275
2276 HRESULT rc = checkStateDependency(MutableStateDep);
2277 if (FAILED(rc)) return rc;
2278
2279 if (!mData->mCurrentSnapshot.isNull())
2280 return setError(E_FAIL,
2281 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2282
2283 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2284
2285 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2286 if (strSnapshotFolder.isEmpty())
2287 strSnapshotFolder = "Snapshots";
2288 int vrc = calculateFullPath(strSnapshotFolder,
2289 strSnapshotFolder);
2290 if (RT_FAILURE(vrc))
2291 return setError(E_FAIL,
2292 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2293 aSnapshotFolder, vrc);
2294
2295 setModified(IsModified_MachineData);
2296 mUserData.backup();
2297
2298 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2299
2300 return S_OK;
2301}
2302
2303STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2304{
2305 CheckComArgOutSafeArrayPointerValid(aAttachments);
2306
2307 AutoCaller autoCaller(this);
2308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2309
2310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2311
2312 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2313 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2314
2315 return S_OK;
2316}
2317
2318STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2319{
2320 CheckComArgOutPointerValid(vrdeServer);
2321
2322 AutoCaller autoCaller(this);
2323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2324
2325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2326
2327 Assert(!!mVRDEServer);
2328 mVRDEServer.queryInterfaceTo(vrdeServer);
2329
2330 return S_OK;
2331}
2332
2333STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2334{
2335 CheckComArgOutPointerValid(audioAdapter);
2336
2337 AutoCaller autoCaller(this);
2338 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2339
2340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2341
2342 mAudioAdapter.queryInterfaceTo(audioAdapter);
2343 return S_OK;
2344}
2345
2346STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2347{
2348#ifdef VBOX_WITH_VUSB
2349 CheckComArgOutPointerValid(aUSBController);
2350
2351 AutoCaller autoCaller(this);
2352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2353
2354 clearError();
2355 MultiResult rc(S_OK);
2356
2357# ifdef VBOX_WITH_USB
2358 rc = mParent->host()->checkUSBProxyService();
2359 if (FAILED(rc)) return rc;
2360# endif
2361
2362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 return rc = mUSBController.queryInterfaceTo(aUSBController);
2365#else
2366 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2367 * extended error info to indicate that USB is simply not available
2368 * (w/o treating it as a failure), for example, as in OSE */
2369 NOREF(aUSBController);
2370 ReturnComNotImplemented();
2371#endif /* VBOX_WITH_VUSB */
2372}
2373
2374STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2375{
2376 CheckComArgOutPointerValid(aFilePath);
2377
2378 AutoLimitedCaller autoCaller(this);
2379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2380
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 mData->m_strConfigFileFull.cloneTo(aFilePath);
2384 return S_OK;
2385}
2386
2387STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2388{
2389 CheckComArgOutPointerValid(aModified);
2390
2391 AutoCaller autoCaller(this);
2392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2393
2394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2395
2396 HRESULT rc = checkStateDependency(MutableStateDep);
2397 if (FAILED(rc)) return rc;
2398
2399 if (!mData->pMachineConfigFile->fileExists())
2400 // this is a new machine, and no config file exists yet:
2401 *aModified = TRUE;
2402 else
2403 *aModified = (mData->flModifications != 0);
2404
2405 return S_OK;
2406}
2407
2408STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2409{
2410 CheckComArgOutPointerValid(aSessionState);
2411
2412 AutoCaller autoCaller(this);
2413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2414
2415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2416
2417 *aSessionState = mData->mSession.mState;
2418
2419 return S_OK;
2420}
2421
2422STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2423{
2424 CheckComArgOutPointerValid(aSessionType);
2425
2426 AutoCaller autoCaller(this);
2427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2428
2429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2430
2431 mData->mSession.mType.cloneTo(aSessionType);
2432
2433 return S_OK;
2434}
2435
2436STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2437{
2438 CheckComArgOutPointerValid(aSessionPid);
2439
2440 AutoCaller autoCaller(this);
2441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2442
2443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2444
2445 *aSessionPid = mData->mSession.mPid;
2446
2447 return S_OK;
2448}
2449
2450STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2451{
2452 CheckComArgOutPointerValid(machineState);
2453
2454 AutoCaller autoCaller(this);
2455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2456
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 *machineState = mData->mMachineState;
2460
2461 return S_OK;
2462}
2463
2464STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2465{
2466 CheckComArgOutPointerValid(aLastStateChange);
2467
2468 AutoCaller autoCaller(this);
2469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2470
2471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2472
2473 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2474
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2479{
2480 CheckComArgOutPointerValid(aStateFilePath);
2481
2482 AutoCaller autoCaller(this);
2483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2484
2485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2488
2489 return S_OK;
2490}
2491
2492STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2493{
2494 CheckComArgOutPointerValid(aLogFolder);
2495
2496 AutoCaller autoCaller(this);
2497 AssertComRCReturnRC(autoCaller.rc());
2498
2499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2500
2501 Utf8Str logFolder;
2502 getLogFolder(logFolder);
2503 logFolder.cloneTo(aLogFolder);
2504
2505 return S_OK;
2506}
2507
2508STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2509{
2510 CheckComArgOutPointerValid(aCurrentSnapshot);
2511
2512 AutoCaller autoCaller(this);
2513 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2514
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2518
2519 return S_OK;
2520}
2521
2522STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2523{
2524 CheckComArgOutPointerValid(aSnapshotCount);
2525
2526 AutoCaller autoCaller(this);
2527 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2528
2529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2530
2531 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2532 ? 0
2533 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2534
2535 return S_OK;
2536}
2537
2538STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2539{
2540 CheckComArgOutPointerValid(aCurrentStateModified);
2541
2542 AutoCaller autoCaller(this);
2543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2544
2545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 /* Note: for machines with no snapshots, we always return FALSE
2548 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2549 * reasons :) */
2550
2551 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2552 ? FALSE
2553 : mData->mCurrentStateModified;
2554
2555 return S_OK;
2556}
2557
2558STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2559{
2560 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2561
2562 AutoCaller autoCaller(this);
2563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2564
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2568 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2569
2570 return S_OK;
2571}
2572
2573STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2574{
2575 CheckComArgOutPointerValid(aClipboardMode);
2576
2577 AutoCaller autoCaller(this);
2578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2579
2580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2581
2582 *aClipboardMode = mHWData->mClipboardMode;
2583
2584 return S_OK;
2585}
2586
2587STDMETHODIMP
2588Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2589{
2590 HRESULT rc = S_OK;
2591
2592 AutoCaller autoCaller(this);
2593 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2594
2595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 alock.release();
2598 rc = onClipboardModeChange(aClipboardMode);
2599 alock.acquire();
2600 if (FAILED(rc)) return rc;
2601
2602 setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mClipboardMode = aClipboardMode;
2605
2606 /* Save settings if online - todo why is this required?? */
2607 if (Global::IsOnline(mData->mMachineState))
2608 saveSettings(NULL);
2609
2610 return S_OK;
2611}
2612
2613STDMETHODIMP
2614Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2615{
2616 CheckComArgOutPointerValid(aPatterns);
2617
2618 AutoCaller autoCaller(this);
2619 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2620
2621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 try
2624 {
2625 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2626 }
2627 catch (...)
2628 {
2629 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2630 }
2631
2632 return S_OK;
2633}
2634
2635STDMETHODIMP
2636Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2637{
2638 AutoCaller autoCaller(this);
2639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2640
2641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 HRESULT rc = checkStateDependency(MutableStateDep);
2644 if (FAILED(rc)) return rc;
2645
2646 setModified(IsModified_MachineData);
2647 mHWData.backup();
2648 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2649 return rc;
2650}
2651
2652STDMETHODIMP
2653Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2654{
2655 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2656
2657 AutoCaller autoCaller(this);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2663 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2664
2665 return S_OK;
2666}
2667
2668STDMETHODIMP
2669Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2670{
2671 CheckComArgOutPointerValid(aEnabled);
2672
2673 AutoCaller autoCaller(this);
2674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2675
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 *aEnabled = mUserData->s.fTeleporterEnabled;
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2684{
2685 AutoCaller autoCaller(this);
2686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2687
2688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2689
2690 /* Only allow it to be set to true when PoweredOff or Aborted.
2691 (Clearing it is always permitted.) */
2692 if ( aEnabled
2693 && mData->mRegistered
2694 && ( !isSessionMachine()
2695 || ( mData->mMachineState != MachineState_PoweredOff
2696 && mData->mMachineState != MachineState_Teleported
2697 && mData->mMachineState != MachineState_Aborted
2698 )
2699 )
2700 )
2701 return setError(VBOX_E_INVALID_VM_STATE,
2702 tr("The machine is not powered off (state is %s)"),
2703 Global::stringifyMachineState(mData->mMachineState));
2704
2705 setModified(IsModified_MachineData);
2706 mUserData.backup();
2707 mUserData->s.fTeleporterEnabled = !!aEnabled;
2708
2709 return S_OK;
2710}
2711
2712STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2713{
2714 CheckComArgOutPointerValid(aPort);
2715
2716 AutoCaller autoCaller(this);
2717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2718
2719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2720
2721 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2722
2723 return S_OK;
2724}
2725
2726STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2727{
2728 if (aPort >= _64K)
2729 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 HRESULT rc = checkStateDependency(MutableStateDep);
2737 if (FAILED(rc)) return rc;
2738
2739 setModified(IsModified_MachineData);
2740 mUserData.backup();
2741 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2742
2743 return S_OK;
2744}
2745
2746STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2747{
2748 CheckComArgOutPointerValid(aAddress);
2749
2750 AutoCaller autoCaller(this);
2751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2752
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2756
2757 return S_OK;
2758}
2759
2760STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2761{
2762 AutoCaller autoCaller(this);
2763 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2764
2765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2766
2767 HRESULT rc = checkStateDependency(MutableStateDep);
2768 if (FAILED(rc)) return rc;
2769
2770 setModified(IsModified_MachineData);
2771 mUserData.backup();
2772 mUserData->s.strTeleporterAddress = aAddress;
2773
2774 return S_OK;
2775}
2776
2777STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2778{
2779 CheckComArgOutPointerValid(aPassword);
2780
2781 AutoCaller autoCaller(this);
2782 HRESULT hrc = autoCaller.rc();
2783 if (SUCCEEDED(hrc))
2784 {
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2787 }
2788
2789 return hrc;
2790}
2791
2792STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2793{
2794 /*
2795 * Hash the password first.
2796 */
2797 Utf8Str strPassword(aPassword);
2798 if (!strPassword.isEmpty())
2799 {
2800 if (VBoxIsPasswordHashed(&strPassword))
2801 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2802 VBoxHashPassword(&strPassword);
2803 }
2804
2805 /*
2806 * Do the update.
2807 */
2808 AutoCaller autoCaller(this);
2809 HRESULT hrc = autoCaller.rc();
2810 if (SUCCEEDED(hrc))
2811 {
2812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2813 hrc = checkStateDependency(MutableStateDep);
2814 if (SUCCEEDED(hrc))
2815 {
2816 setModified(IsModified_MachineData);
2817 mUserData.backup();
2818 mUserData->s.strTeleporterPassword = strPassword;
2819 }
2820 }
2821
2822 return hrc;
2823}
2824
2825STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2826{
2827 CheckComArgOutPointerValid(aState);
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 *aState = mUserData->s.enmFaultToleranceState;
2835 return S_OK;
2836}
2837
2838STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2839{
2840 AutoCaller autoCaller(this);
2841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2842
2843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2844
2845 /* @todo deal with running state change. */
2846 HRESULT rc = checkStateDependency(MutableStateDep);
2847 if (FAILED(rc)) return rc;
2848
2849 setModified(IsModified_MachineData);
2850 mUserData.backup();
2851 mUserData->s.enmFaultToleranceState = aState;
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2856{
2857 CheckComArgOutPointerValid(aAddress);
2858
2859 AutoCaller autoCaller(this);
2860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2861
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2865 return S_OK;
2866}
2867
2868STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2869{
2870 AutoCaller autoCaller(this);
2871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2872
2873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 /* @todo deal with running state change. */
2876 HRESULT rc = checkStateDependency(MutableStateDep);
2877 if (FAILED(rc)) return rc;
2878
2879 setModified(IsModified_MachineData);
2880 mUserData.backup();
2881 mUserData->s.strFaultToleranceAddress = aAddress;
2882 return S_OK;
2883}
2884
2885STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2886{
2887 CheckComArgOutPointerValid(aPort);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aPort = mUserData->s.uFaultTolerancePort;
2895 return S_OK;
2896}
2897
2898STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2899{
2900 AutoCaller autoCaller(this);
2901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2902
2903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 /* @todo deal with running state change. */
2906 HRESULT rc = checkStateDependency(MutableStateDep);
2907 if (FAILED(rc)) return rc;
2908
2909 setModified(IsModified_MachineData);
2910 mUserData.backup();
2911 mUserData->s.uFaultTolerancePort = aPort;
2912 return S_OK;
2913}
2914
2915STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2916{
2917 CheckComArgOutPointerValid(aPassword);
2918
2919 AutoCaller autoCaller(this);
2920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2921
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2925
2926 return S_OK;
2927}
2928
2929STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2930{
2931 AutoCaller autoCaller(this);
2932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2933
2934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2935
2936 /* @todo deal with running state change. */
2937 HRESULT rc = checkStateDependency(MutableStateDep);
2938 if (FAILED(rc)) return rc;
2939
2940 setModified(IsModified_MachineData);
2941 mUserData.backup();
2942 mUserData->s.strFaultTolerancePassword = aPassword;
2943
2944 return S_OK;
2945}
2946
2947STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2948{
2949 CheckComArgOutPointerValid(aInterval);
2950
2951 AutoCaller autoCaller(this);
2952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2953
2954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2955
2956 *aInterval = mUserData->s.uFaultToleranceInterval;
2957 return S_OK;
2958}
2959
2960STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
2961{
2962 AutoCaller autoCaller(this);
2963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2964
2965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 /* @todo deal with running state change. */
2968 HRESULT rc = checkStateDependency(MutableStateDep);
2969 if (FAILED(rc)) return rc;
2970
2971 setModified(IsModified_MachineData);
2972 mUserData.backup();
2973 mUserData->s.uFaultToleranceInterval = aInterval;
2974 return S_OK;
2975}
2976
2977STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2978{
2979 CheckComArgOutPointerValid(aEnabled);
2980
2981 AutoCaller autoCaller(this);
2982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2983
2984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2985
2986 *aEnabled = mUserData->s.fRTCUseUTC;
2987
2988 return S_OK;
2989}
2990
2991STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2992{
2993 AutoCaller autoCaller(this);
2994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2995
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 /* Only allow it to be set to true when PoweredOff or Aborted.
2999 (Clearing it is always permitted.) */
3000 if ( aEnabled
3001 && mData->mRegistered
3002 && ( !isSessionMachine()
3003 || ( mData->mMachineState != MachineState_PoweredOff
3004 && mData->mMachineState != MachineState_Teleported
3005 && mData->mMachineState != MachineState_Aborted
3006 )
3007 )
3008 )
3009 return setError(VBOX_E_INVALID_VM_STATE,
3010 tr("The machine is not powered off (state is %s)"),
3011 Global::stringifyMachineState(mData->mMachineState));
3012
3013 setModified(IsModified_MachineData);
3014 mUserData.backup();
3015 mUserData->s.fRTCUseUTC = !!aEnabled;
3016
3017 return S_OK;
3018}
3019
3020STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
3021{
3022 CheckComArgOutPointerValid(aEnabled);
3023
3024 AutoCaller autoCaller(this);
3025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3026
3027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3028
3029 *aEnabled = mHWData->mIoCacheEnabled;
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
3035{
3036 AutoCaller autoCaller(this);
3037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3038
3039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3040
3041 HRESULT rc = checkStateDependency(MutableStateDep);
3042 if (FAILED(rc)) return rc;
3043
3044 setModified(IsModified_MachineData);
3045 mHWData.backup();
3046 mHWData->mIoCacheEnabled = aEnabled;
3047
3048 return S_OK;
3049}
3050
3051STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
3052{
3053 CheckComArgOutPointerValid(aIoCacheSize);
3054
3055 AutoCaller autoCaller(this);
3056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3057
3058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3059
3060 *aIoCacheSize = mHWData->mIoCacheSize;
3061
3062 return S_OK;
3063}
3064
3065STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
3066{
3067 AutoCaller autoCaller(this);
3068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3069
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 HRESULT rc = checkStateDependency(MutableStateDep);
3073 if (FAILED(rc)) return rc;
3074
3075 setModified(IsModified_MachineData);
3076 mHWData.backup();
3077 mHWData->mIoCacheSize = aIoCacheSize;
3078
3079 return S_OK;
3080}
3081
3082
3083/**
3084 * @note Locks objects!
3085 */
3086STDMETHODIMP Machine::LockMachine(ISession *aSession,
3087 LockType_T lockType)
3088{
3089 CheckComArgNotNull(aSession);
3090
3091 AutoCaller autoCaller(this);
3092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3093
3094 /* check the session state */
3095 SessionState_T state;
3096 HRESULT rc = aSession->COMGETTER(State)(&state);
3097 if (FAILED(rc)) return rc;
3098
3099 if (state != SessionState_Unlocked)
3100 return setError(VBOX_E_INVALID_OBJECT_STATE,
3101 tr("The given session is busy"));
3102
3103 // get the client's IInternalSessionControl interface
3104 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3105 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3106 E_INVALIDARG);
3107
3108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 if (!mData->mRegistered)
3111 return setError(E_UNEXPECTED,
3112 tr("The machine '%s' is not registered"),
3113 mUserData->s.strName.c_str());
3114
3115 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3116
3117 SessionState_T oldState = mData->mSession.mState;
3118 /* Hack: in case the session is closing and there is a progress object
3119 * which allows waiting for the session to be closed, take the opportunity
3120 * and do a limited wait (max. 1 second). This helps a lot when the system
3121 * is busy and thus session closing can take a little while. */
3122 if ( mData->mSession.mState == SessionState_Unlocking
3123 && mData->mSession.mProgress)
3124 {
3125 alock.release();
3126 mData->mSession.mProgress->WaitForCompletion(1000);
3127 alock.acquire();
3128 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3129 }
3130
3131 // try again now
3132 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3133 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3134 )
3135 {
3136 // OK, share the session... we are now dealing with three processes:
3137 // 1) VBoxSVC (where this code runs);
3138 // 2) process C: the caller's client process (who wants a shared session);
3139 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3140
3141 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3142 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3143 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3144 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3145 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3146
3147 /*
3148 * Release the lock before calling the client process. It's safe here
3149 * since the only thing to do after we get the lock again is to add
3150 * the remote control to the list (which doesn't directly influence
3151 * anything).
3152 */
3153 alock.release();
3154
3155 // get the console of the session holding the write lock (this is a remote call)
3156 ComPtr<IConsole> pConsoleW;
3157 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3158 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3159 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3160 if (FAILED(rc))
3161 // the failure may occur w/o any error info (from RPC), so provide one
3162 return setError(VBOX_E_VM_ERROR,
3163 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3164
3165 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3166
3167 // share the session machine and W's console with the caller's session
3168 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3169 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3170 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3171
3172 if (FAILED(rc))
3173 // the failure may occur w/o any error info (from RPC), so provide one
3174 return setError(VBOX_E_VM_ERROR,
3175 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3176 alock.acquire();
3177
3178 // need to revalidate the state after acquiring the lock again
3179 if (mData->mSession.mState != SessionState_Locked)
3180 {
3181 pSessionControl->Uninitialize();
3182 return setError(VBOX_E_INVALID_SESSION_STATE,
3183 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3184 mUserData->s.strName.c_str());
3185 }
3186
3187 // add the caller's session to the list
3188 mData->mSession.mRemoteControls.push_back(pSessionControl);
3189 }
3190 else if ( mData->mSession.mState == SessionState_Locked
3191 || mData->mSession.mState == SessionState_Unlocking
3192 )
3193 {
3194 // sharing not permitted, or machine still unlocking:
3195 return setError(VBOX_E_INVALID_OBJECT_STATE,
3196 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3197 mUserData->s.strName.c_str());
3198 }
3199 else
3200 {
3201 // machine is not locked: then write-lock the machine (create the session machine)
3202
3203 // must not be busy
3204 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3205
3206 // get the caller's session PID
3207 RTPROCESS pid = NIL_RTPROCESS;
3208 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3209 pSessionControl->GetPID((ULONG*)&pid);
3210 Assert(pid != NIL_RTPROCESS);
3211
3212 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3213
3214 if (fLaunchingVMProcess)
3215 {
3216 // this machine is awaiting for a spawning session to be opened:
3217 // then the calling process must be the one that got started by
3218 // LaunchVMProcess()
3219
3220 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
3221 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3222
3223 if (mData->mSession.mPid != pid)
3224 return setError(E_ACCESSDENIED,
3225 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3226 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3227 pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
3228 }
3229
3230 // create the mutable SessionMachine from the current machine
3231 ComObjPtr<SessionMachine> sessionMachine;
3232 sessionMachine.createObject();
3233 rc = sessionMachine->init(this);
3234 AssertComRC(rc);
3235
3236 /* NOTE: doing return from this function after this point but
3237 * before the end is forbidden since it may call SessionMachine::uninit()
3238 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3239 * lock while still holding the Machine lock in alock so that a deadlock
3240 * is possible due to the wrong lock order. */
3241
3242 if (SUCCEEDED(rc))
3243 {
3244 /*
3245 * Set the session state to Spawning to protect against subsequent
3246 * attempts to open a session and to unregister the machine after
3247 * we release the lock.
3248 */
3249 SessionState_T origState = mData->mSession.mState;
3250 mData->mSession.mState = SessionState_Spawning;
3251
3252 /*
3253 * Release the lock before calling the client process -- it will call
3254 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3255 * because the state is Spawning, so that LaunchVMProcess() and
3256 * LockMachine() calls will fail. This method, called before we
3257 * acquire the lock again, will fail because of the wrong PID.
3258 *
3259 * Note that mData->mSession.mRemoteControls accessed outside
3260 * the lock may not be modified when state is Spawning, so it's safe.
3261 */
3262 alock.release();
3263
3264 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3265 rc = pSessionControl->AssignMachine(sessionMachine);
3266 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3267
3268 /* The failure may occur w/o any error info (from RPC), so provide one */
3269 if (FAILED(rc))
3270 setError(VBOX_E_VM_ERROR,
3271 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3272
3273 if ( SUCCEEDED(rc)
3274 && fLaunchingVMProcess
3275 )
3276 {
3277 /* complete the remote session initialization */
3278
3279 /* get the console from the direct session */
3280 ComPtr<IConsole> console;
3281 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3282 ComAssertComRC(rc);
3283
3284 if (SUCCEEDED(rc) && !console)
3285 {
3286 ComAssert(!!console);
3287 rc = E_FAIL;
3288 }
3289
3290 /* assign machine & console to the remote session */
3291 if (SUCCEEDED(rc))
3292 {
3293 /*
3294 * after LaunchVMProcess(), the first and the only
3295 * entry in remoteControls is that remote session
3296 */
3297 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3298 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3299 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3300
3301 /* The failure may occur w/o any error info (from RPC), so provide one */
3302 if (FAILED(rc))
3303 setError(VBOX_E_VM_ERROR,
3304 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3305 }
3306
3307 if (FAILED(rc))
3308 pSessionControl->Uninitialize();
3309 }
3310
3311 /* acquire the lock again */
3312 alock.acquire();
3313
3314 /* Restore the session state */
3315 mData->mSession.mState = origState;
3316 }
3317
3318 // finalize spawning anyway (this is why we don't return on errors above)
3319 if (fLaunchingVMProcess)
3320 {
3321 /* Note that the progress object is finalized later */
3322 /** @todo Consider checking mData->mSession.mProgress for cancellation
3323 * around here. */
3324
3325 /* We don't reset mSession.mPid here because it is necessary for
3326 * SessionMachine::uninit() to reap the child process later. */
3327
3328 if (FAILED(rc))
3329 {
3330 /* Close the remote session, remove the remote control from the list
3331 * and reset session state to Closed (@note keep the code in sync
3332 * with the relevant part in openSession()). */
3333
3334 Assert(mData->mSession.mRemoteControls.size() == 1);
3335 if (mData->mSession.mRemoteControls.size() == 1)
3336 {
3337 ErrorInfoKeeper eik;
3338 mData->mSession.mRemoteControls.front()->Uninitialize();
3339 }
3340
3341 mData->mSession.mRemoteControls.clear();
3342 mData->mSession.mState = SessionState_Unlocked;
3343 }
3344 }
3345 else
3346 {
3347 /* memorize PID of the directly opened session */
3348 if (SUCCEEDED(rc))
3349 mData->mSession.mPid = pid;
3350 }
3351
3352 if (SUCCEEDED(rc))
3353 {
3354 /* memorize the direct session control and cache IUnknown for it */
3355 mData->mSession.mDirectControl = pSessionControl;
3356 mData->mSession.mState = SessionState_Locked;
3357 /* associate the SessionMachine with this Machine */
3358 mData->mSession.mMachine = sessionMachine;
3359
3360 /* request an IUnknown pointer early from the remote party for later
3361 * identity checks (it will be internally cached within mDirectControl
3362 * at least on XPCOM) */
3363 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3364 NOREF(unk);
3365 }
3366
3367 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3368 * would break the lock order */
3369 alock.release();
3370
3371 /* uninitialize the created session machine on failure */
3372 if (FAILED(rc))
3373 sessionMachine->uninit();
3374
3375 }
3376
3377 if (SUCCEEDED(rc))
3378 {
3379 /*
3380 * tell the client watcher thread to update the set of
3381 * machines that have open sessions
3382 */
3383 mParent->updateClientWatcher();
3384
3385 if (oldState != SessionState_Locked)
3386 /* fire an event */
3387 mParent->onSessionStateChange(getId(), SessionState_Locked);
3388 }
3389
3390 return rc;
3391}
3392
3393/**
3394 * @note Locks objects!
3395 */
3396STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3397 IN_BSTR aType,
3398 IN_BSTR aEnvironment,
3399 IProgress **aProgress)
3400{
3401 CheckComArgStrNotEmptyOrNull(aType);
3402 Utf8Str strType(aType);
3403 Utf8Str strEnvironment(aEnvironment);
3404 /* "emergencystop" doesn't need the session, so skip the checks/interface
3405 * retrieval. This code doesn't quite fit in here, but introducing a
3406 * special API method would be even more effort, and would require explicit
3407 * support by every API client. It's better to hide the feature a bit. */
3408 if (strType != "emergencystop")
3409 CheckComArgNotNull(aSession);
3410 CheckComArgOutPointerValid(aProgress);
3411
3412 AutoCaller autoCaller(this);
3413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3414
3415 ComPtr<IInternalSessionControl> control;
3416 HRESULT rc = S_OK;
3417
3418 if (strType != "emergencystop")
3419 {
3420 /* check the session state */
3421 SessionState_T state;
3422 rc = aSession->COMGETTER(State)(&state);
3423 if (FAILED(rc))
3424 return rc;
3425
3426 if (state != SessionState_Unlocked)
3427 return setError(VBOX_E_INVALID_OBJECT_STATE,
3428 tr("The given session is busy"));
3429
3430 /* get the IInternalSessionControl interface */
3431 control = aSession;
3432 ComAssertMsgRet(!control.isNull(),
3433 ("No IInternalSessionControl interface"),
3434 E_INVALIDARG);
3435 }
3436
3437 /* get the teleporter enable state for the progress object init. */
3438 BOOL fTeleporterEnabled;
3439 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3440 if (FAILED(rc))
3441 return rc;
3442
3443 /* create a progress object */
3444 if (strType != "emergencystop")
3445 {
3446 ComObjPtr<ProgressProxy> progress;
3447 progress.createObject();
3448 rc = progress->init(mParent,
3449 static_cast<IMachine*>(this),
3450 Bstr(tr("Starting VM")).raw(),
3451 TRUE /* aCancelable */,
3452 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3453 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3454 2 /* uFirstOperationWeight */,
3455 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3456
3457 if (SUCCEEDED(rc))
3458 {
3459 rc = launchVMProcess(control, strType, strEnvironment, progress);
3460 if (SUCCEEDED(rc))
3461 {
3462 progress.queryInterfaceTo(aProgress);
3463
3464 /* signal the client watcher thread */
3465 mParent->updateClientWatcher();
3466
3467 /* fire an event */
3468 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3469 }
3470 }
3471 }
3472 else
3473 {
3474 /* no progress object - either instant success or failure */
3475 *aProgress = NULL;
3476
3477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3478
3479 if (mData->mSession.mState != SessionState_Locked)
3480 return setError(VBOX_E_INVALID_OBJECT_STATE,
3481 tr("The machine '%s' is not locked by a session"),
3482 mUserData->s.strName.c_str());
3483
3484 /* must have a VM process associated - do not kill normal API clients
3485 * with an open session */
3486 if (!Global::IsOnline(mData->mMachineState))
3487 return setError(VBOX_E_INVALID_OBJECT_STATE,
3488 tr("The machine '%s' does not have a VM process"),
3489 mUserData->s.strName.c_str());
3490
3491 /* forcibly terminate the VM process */
3492 if (mData->mSession.mPid != NIL_RTPROCESS)
3493 RTProcTerminate(mData->mSession.mPid);
3494
3495 /* signal the client watcher thread, as most likely the client has
3496 * been terminated */
3497 mParent->updateClientWatcher();
3498 }
3499
3500 return rc;
3501}
3502
3503STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3504{
3505 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3506 return setError(E_INVALIDARG,
3507 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3508 aPosition, SchemaDefs::MaxBootPosition);
3509
3510 if (aDevice == DeviceType_USB)
3511 return setError(E_NOTIMPL,
3512 tr("Booting from USB device is currently not supported"));
3513
3514 AutoCaller autoCaller(this);
3515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3516
3517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3518
3519 HRESULT rc = checkStateDependency(MutableStateDep);
3520 if (FAILED(rc)) return rc;
3521
3522 setModified(IsModified_MachineData);
3523 mHWData.backup();
3524 mHWData->mBootOrder[aPosition - 1] = aDevice;
3525
3526 return S_OK;
3527}
3528
3529STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3530{
3531 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3532 return setError(E_INVALIDARG,
3533 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3534 aPosition, SchemaDefs::MaxBootPosition);
3535
3536 AutoCaller autoCaller(this);
3537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3538
3539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3540
3541 *aDevice = mHWData->mBootOrder[aPosition - 1];
3542
3543 return S_OK;
3544}
3545
3546STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3547 LONG aControllerPort,
3548 LONG aDevice,
3549 DeviceType_T aType,
3550 IMedium *aMedium)
3551{
3552 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3553 aControllerName, aControllerPort, aDevice, aType, aMedium));
3554
3555 CheckComArgStrNotEmptyOrNull(aControllerName);
3556
3557 AutoCaller autoCaller(this);
3558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3559
3560 // request the host lock first, since might be calling Host methods for getting host drives;
3561 // next, protect the media tree all the while we're in here, as well as our member variables
3562 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3563 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3564
3565 HRESULT rc = checkStateDependency(MutableStateDep);
3566 if (FAILED(rc)) return rc;
3567
3568 /// @todo NEWMEDIA implicit machine registration
3569 if (!mData->mRegistered)
3570 return setError(VBOX_E_INVALID_OBJECT_STATE,
3571 tr("Cannot attach storage devices to an unregistered machine"));
3572
3573 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3574
3575 /* Check for an existing controller. */
3576 ComObjPtr<StorageController> ctl;
3577 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3578 if (FAILED(rc)) return rc;
3579
3580 StorageControllerType_T ctrlType;
3581 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3582 if (FAILED(rc))
3583 return setError(E_FAIL,
3584 tr("Could not get type of controller '%ls'"),
3585 aControllerName);
3586
3587 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3588 bool fHotplug = false;
3589 if (Global::IsOnlineOrTransient(mData->mMachineState))
3590 fHotplug = true;
3591
3592 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3593 return setError(VBOX_E_INVALID_VM_STATE,
3594 tr("Controller '%ls' does not support hotplugging"),
3595 aControllerName);
3596
3597 // check that the port and device are not out of range
3598 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3599 if (FAILED(rc)) return rc;
3600
3601 /* check if the device slot is already busy */
3602 MediumAttachment *pAttachTemp;
3603 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3604 aControllerName,
3605 aControllerPort,
3606 aDevice)))
3607 {
3608 Medium *pMedium = pAttachTemp->getMedium();
3609 if (pMedium)
3610 {
3611 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3612 return setError(VBOX_E_OBJECT_IN_USE,
3613 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3614 pMedium->getLocationFull().c_str(),
3615 aControllerPort,
3616 aDevice,
3617 aControllerName);
3618 }
3619 else
3620 return setError(VBOX_E_OBJECT_IN_USE,
3621 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3622 aControllerPort, aDevice, aControllerName);
3623 }
3624
3625 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3626 if (aMedium && medium.isNull())
3627 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3628
3629 AutoCaller mediumCaller(medium);
3630 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3631
3632 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3633
3634 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3635 && !medium.isNull()
3636 )
3637 return setError(VBOX_E_OBJECT_IN_USE,
3638 tr("Medium '%s' is already attached to this virtual machine"),
3639 medium->getLocationFull().c_str());
3640
3641 if (!medium.isNull())
3642 {
3643 MediumType_T mtype = medium->getType();
3644 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3645 // For DVDs it's not written to the config file, so needs no global config
3646 // version bump. For floppies it's a new attribute "type", which is ignored
3647 // by older VirtualBox version, so needs no global config version bump either.
3648 // For hard disks this type is not accepted.
3649 if (mtype == MediumType_MultiAttach)
3650 {
3651 // This type is new with VirtualBox 4.0 and therefore requires settings
3652 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3653 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3654 // two reasons: The medium type is a property of the media registry tree, which
3655 // can reside in the global config file (for pre-4.0 media); we would therefore
3656 // possibly need to bump the global config version. We don't want to do that though
3657 // because that might make downgrading to pre-4.0 impossible.
3658 // As a result, we can only use these two new types if the medium is NOT in the
3659 // global registry:
3660 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3661 if ( medium->isInRegistry(uuidGlobalRegistry)
3662 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3663 )
3664 return setError(VBOX_E_INVALID_OBJECT_STATE,
3665 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3666 "to machines that were created with VirtualBox 4.0 or later"),
3667 medium->getLocationFull().c_str());
3668 }
3669 }
3670
3671 bool fIndirect = false;
3672 if (!medium.isNull())
3673 fIndirect = medium->isReadOnly();
3674 bool associate = true;
3675
3676 do
3677 {
3678 if ( aType == DeviceType_HardDisk
3679 && mMediaData.isBackedUp())
3680 {
3681 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3682
3683 /* check if the medium was attached to the VM before we started
3684 * changing attachments in which case the attachment just needs to
3685 * be restored */
3686 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3687 {
3688 AssertReturn(!fIndirect, E_FAIL);
3689
3690 /* see if it's the same bus/channel/device */
3691 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3692 {
3693 /* the simplest case: restore the whole attachment
3694 * and return, nothing else to do */
3695 mMediaData->mAttachments.push_back(pAttachTemp);
3696 return S_OK;
3697 }
3698
3699 /* bus/channel/device differ; we need a new attachment object,
3700 * but don't try to associate it again */
3701 associate = false;
3702 break;
3703 }
3704 }
3705
3706 /* go further only if the attachment is to be indirect */
3707 if (!fIndirect)
3708 break;
3709
3710 /* perform the so called smart attachment logic for indirect
3711 * attachments. Note that smart attachment is only applicable to base
3712 * hard disks. */
3713
3714 if (medium->getParent().isNull())
3715 {
3716 /* first, investigate the backup copy of the current hard disk
3717 * attachments to make it possible to re-attach existing diffs to
3718 * another device slot w/o losing their contents */
3719 if (mMediaData.isBackedUp())
3720 {
3721 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3722
3723 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3724 uint32_t foundLevel = 0;
3725
3726 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3727 it != oldAtts.end();
3728 ++it)
3729 {
3730 uint32_t level = 0;
3731 MediumAttachment *pAttach = *it;
3732 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3733 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3734 if (pMedium.isNull())
3735 continue;
3736
3737 if (pMedium->getBase(&level) == medium)
3738 {
3739 /* skip the hard disk if its currently attached (we
3740 * cannot attach the same hard disk twice) */
3741 if (findAttachment(mMediaData->mAttachments,
3742 pMedium))
3743 continue;
3744
3745 /* matched device, channel and bus (i.e. attached to the
3746 * same place) will win and immediately stop the search;
3747 * otherwise the attachment that has the youngest
3748 * descendant of medium will be used
3749 */
3750 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3751 {
3752 /* the simplest case: restore the whole attachment
3753 * and return, nothing else to do */
3754 mMediaData->mAttachments.push_back(*it);
3755 return S_OK;
3756 }
3757 else if ( foundIt == oldAtts.end()
3758 || level > foundLevel /* prefer younger */
3759 )
3760 {
3761 foundIt = it;
3762 foundLevel = level;
3763 }
3764 }
3765 }
3766
3767 if (foundIt != oldAtts.end())
3768 {
3769 /* use the previously attached hard disk */
3770 medium = (*foundIt)->getMedium();
3771 mediumCaller.attach(medium);
3772 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3773 mediumLock.attach(medium);
3774 /* not implicit, doesn't require association with this VM */
3775 fIndirect = false;
3776 associate = false;
3777 /* go right to the MediumAttachment creation */
3778 break;
3779 }
3780 }
3781
3782 /* must give up the medium lock and medium tree lock as below we
3783 * go over snapshots, which needs a lock with higher lock order. */
3784 mediumLock.release();
3785 treeLock.release();
3786
3787 /* then, search through snapshots for the best diff in the given
3788 * hard disk's chain to base the new diff on */
3789
3790 ComObjPtr<Medium> base;
3791 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3792 while (snap)
3793 {
3794 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3795
3796 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3797
3798 MediumAttachment *pAttachFound = NULL;
3799 uint32_t foundLevel = 0;
3800
3801 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3802 it != snapAtts.end();
3803 ++it)
3804 {
3805 MediumAttachment *pAttach = *it;
3806 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3807 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3808 if (pMedium.isNull())
3809 continue;
3810
3811 uint32_t level = 0;
3812 if (pMedium->getBase(&level) == medium)
3813 {
3814 /* matched device, channel and bus (i.e. attached to the
3815 * same place) will win and immediately stop the search;
3816 * otherwise the attachment that has the youngest
3817 * descendant of medium will be used
3818 */
3819 if ( pAttach->getDevice() == aDevice
3820 && pAttach->getPort() == aControllerPort
3821 && pAttach->getControllerName() == aControllerName
3822 )
3823 {
3824 pAttachFound = pAttach;
3825 break;
3826 }
3827 else if ( !pAttachFound
3828 || level > foundLevel /* prefer younger */
3829 )
3830 {
3831 pAttachFound = pAttach;
3832 foundLevel = level;
3833 }
3834 }
3835 }
3836
3837 if (pAttachFound)
3838 {
3839 base = pAttachFound->getMedium();
3840 break;
3841 }
3842
3843 snap = snap->getParent();
3844 }
3845
3846 /* re-lock medium tree and the medium, as we need it below */
3847 treeLock.acquire();
3848 mediumLock.acquire();
3849
3850 /* found a suitable diff, use it as a base */
3851 if (!base.isNull())
3852 {
3853 medium = base;
3854 mediumCaller.attach(medium);
3855 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3856 mediumLock.attach(medium);
3857 }
3858 }
3859
3860 Utf8Str strFullSnapshotFolder;
3861 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3862
3863 ComObjPtr<Medium> diff;
3864 diff.createObject();
3865 // store this diff in the same registry as the parent
3866 Guid uuidRegistryParent;
3867 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
3868 {
3869 // parent image has no registry: this can happen if we're attaching a new immutable
3870 // image that has not yet been attached (medium then points to the base and we're
3871 // creating the diff image for the immutable, and the parent is not yet registered);
3872 // put the parent in the machine registry then
3873 mediumLock.release();
3874 treeLock.release();
3875 alock.release();
3876 addMediumToRegistry(medium);
3877 alock.acquire();
3878 treeLock.acquire();
3879 mediumLock.acquire();
3880 medium->getFirstRegistryMachineId(uuidRegistryParent);
3881 }
3882 rc = diff->init(mParent,
3883 medium->getPreferredDiffFormat(),
3884 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3885 uuidRegistryParent);
3886 if (FAILED(rc)) return rc;
3887
3888 /* Apply the normal locking logic to the entire chain. */
3889 MediumLockList *pMediumLockList(new MediumLockList());
3890 mediumLock.release();
3891 treeLock.release();
3892 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3893 true /* fMediumLockWrite */,
3894 medium,
3895 *pMediumLockList);
3896 treeLock.acquire();
3897 mediumLock.acquire();
3898 if (SUCCEEDED(rc))
3899 {
3900 mediumLock.release();
3901 treeLock.release();
3902 rc = pMediumLockList->Lock();
3903 treeLock.acquire();
3904 mediumLock.acquire();
3905 if (FAILED(rc))
3906 setError(rc,
3907 tr("Could not lock medium when creating diff '%s'"),
3908 diff->getLocationFull().c_str());
3909 else
3910 {
3911 /* will release the lock before the potentially lengthy
3912 * operation, so protect with the special state */
3913 MachineState_T oldState = mData->mMachineState;
3914 setMachineState(MachineState_SettingUp);
3915
3916 mediumLock.release();
3917 treeLock.release();
3918 alock.release();
3919
3920 rc = medium->createDiffStorage(diff,
3921 MediumVariant_Standard,
3922 pMediumLockList,
3923 NULL /* aProgress */,
3924 true /* aWait */);
3925
3926 alock.acquire();
3927 treeLock.acquire();
3928 mediumLock.acquire();
3929
3930 setMachineState(oldState);
3931 }
3932 }
3933
3934 /* Unlock the media and free the associated memory. */
3935 delete pMediumLockList;
3936
3937 if (FAILED(rc)) return rc;
3938
3939 /* use the created diff for the actual attachment */
3940 medium = diff;
3941 mediumCaller.attach(medium);
3942 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3943 mediumLock.attach(medium);
3944 }
3945 while (0);
3946
3947 ComObjPtr<MediumAttachment> attachment;
3948 attachment.createObject();
3949 rc = attachment->init(this,
3950 medium,
3951 aControllerName,
3952 aControllerPort,
3953 aDevice,
3954 aType,
3955 fIndirect,
3956 false /* fPassthrough */,
3957 false /* fTempEject */,
3958 false /* fNonRotational */,
3959 false /* fDiscard */,
3960 Utf8Str::Empty);
3961 if (FAILED(rc)) return rc;
3962
3963 if (associate && !medium.isNull())
3964 {
3965 // as the last step, associate the medium to the VM
3966 rc = medium->addBackReference(mData->mUuid);
3967 // here we can fail because of Deleting, or being in process of creating a Diff
3968 if (FAILED(rc)) return rc;
3969
3970 mediumLock.release();
3971 treeLock.release();
3972 alock.release();
3973 addMediumToRegistry(medium);
3974 alock.acquire();
3975 treeLock.acquire();
3976 mediumLock.acquire();
3977 }
3978
3979 /* success: finally remember the attachment */
3980 setModified(IsModified_Storage);
3981 mMediaData.backup();
3982 mMediaData->mAttachments.push_back(attachment);
3983
3984 mediumLock.release();
3985 treeLock.release();
3986 alock.release();
3987
3988 if (fHotplug)
3989 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
3990
3991 mParent->saveModifiedRegistries();
3992
3993 return rc;
3994}
3995
3996STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3997 LONG aDevice)
3998{
3999 CheckComArgStrNotEmptyOrNull(aControllerName);
4000
4001 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4002 aControllerName, aControllerPort, aDevice));
4003
4004 AutoCaller autoCaller(this);
4005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4006
4007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4008
4009 HRESULT rc = checkStateDependency(MutableStateDep);
4010 if (FAILED(rc)) return rc;
4011
4012 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4013
4014 /* Check for an existing controller. */
4015 ComObjPtr<StorageController> ctl;
4016 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4017 if (FAILED(rc)) return rc;
4018
4019 StorageControllerType_T ctrlType;
4020 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4021 if (FAILED(rc))
4022 return setError(E_FAIL,
4023 tr("Could not get type of controller '%ls'"),
4024 aControllerName);
4025
4026 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4027 bool fHotplug = false;
4028 if (Global::IsOnlineOrTransient(mData->mMachineState))
4029 fHotplug = true;
4030
4031 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4032 return setError(VBOX_E_INVALID_VM_STATE,
4033 tr("Controller '%ls' does not support hotplugging"),
4034 aControllerName);
4035
4036 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4037 aControllerName,
4038 aControllerPort,
4039 aDevice);
4040 if (!pAttach)
4041 return setError(VBOX_E_OBJECT_NOT_FOUND,
4042 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4043 aDevice, aControllerPort, aControllerName);
4044
4045 /*
4046 * The VM has to detach the device before we delete any implicit diffs.
4047 * If this fails we can roll back without loosing data.
4048 */
4049 if (fHotplug)
4050 {
4051 alock.release();
4052 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4053 alock.acquire();
4054 }
4055 if (FAILED(rc)) return rc;
4056
4057 /* If we are here everything went well and we can delete the implicit now. */
4058 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4059
4060 alock.release();
4061
4062 mParent->saveModifiedRegistries();
4063
4064 return rc;
4065}
4066
4067STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4068 LONG aDevice, BOOL aPassthrough)
4069{
4070 CheckComArgStrNotEmptyOrNull(aControllerName);
4071
4072 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4073 aControllerName, aControllerPort, aDevice, aPassthrough));
4074
4075 AutoCaller autoCaller(this);
4076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4077
4078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4079
4080 HRESULT rc = checkStateDependency(MutableStateDep);
4081 if (FAILED(rc)) return rc;
4082
4083 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4084
4085 if (Global::IsOnlineOrTransient(mData->mMachineState))
4086 return setError(VBOX_E_INVALID_VM_STATE,
4087 tr("Invalid machine state: %s"),
4088 Global::stringifyMachineState(mData->mMachineState));
4089
4090 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4091 aControllerName,
4092 aControllerPort,
4093 aDevice);
4094 if (!pAttach)
4095 return setError(VBOX_E_OBJECT_NOT_FOUND,
4096 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4097 aDevice, aControllerPort, aControllerName);
4098
4099
4100 setModified(IsModified_Storage);
4101 mMediaData.backup();
4102
4103 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4104
4105 if (pAttach->getType() != DeviceType_DVD)
4106 return setError(E_INVALIDARG,
4107 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4108 aDevice, aControllerPort, aControllerName);
4109 pAttach->updatePassthrough(!!aPassthrough);
4110
4111 return S_OK;
4112}
4113
4114STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4115 LONG aDevice, BOOL aTemporaryEject)
4116{
4117 CheckComArgStrNotEmptyOrNull(aControllerName);
4118
4119 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4120 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4121
4122 AutoCaller autoCaller(this);
4123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4124
4125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4126
4127 HRESULT rc = checkStateDependency(MutableStateDep);
4128 if (FAILED(rc)) return rc;
4129
4130 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4131 aControllerName,
4132 aControllerPort,
4133 aDevice);
4134 if (!pAttach)
4135 return setError(VBOX_E_OBJECT_NOT_FOUND,
4136 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4137 aDevice, aControllerPort, aControllerName);
4138
4139
4140 setModified(IsModified_Storage);
4141 mMediaData.backup();
4142
4143 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4144
4145 if (pAttach->getType() != DeviceType_DVD)
4146 return setError(E_INVALIDARG,
4147 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4148 aDevice, aControllerPort, aControllerName);
4149 pAttach->updateTempEject(!!aTemporaryEject);
4150
4151 return S_OK;
4152}
4153
4154STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4155 LONG aDevice, BOOL aNonRotational)
4156{
4157 CheckComArgStrNotEmptyOrNull(aControllerName);
4158
4159 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4160 aControllerName, aControllerPort, aDevice, aNonRotational));
4161
4162 AutoCaller autoCaller(this);
4163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4164
4165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4166
4167 HRESULT rc = checkStateDependency(MutableStateDep);
4168 if (FAILED(rc)) return rc;
4169
4170 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4171
4172 if (Global::IsOnlineOrTransient(mData->mMachineState))
4173 return setError(VBOX_E_INVALID_VM_STATE,
4174 tr("Invalid machine state: %s"),
4175 Global::stringifyMachineState(mData->mMachineState));
4176
4177 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4178 aControllerName,
4179 aControllerPort,
4180 aDevice);
4181 if (!pAttach)
4182 return setError(VBOX_E_OBJECT_NOT_FOUND,
4183 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4184 aDevice, aControllerPort, aControllerName);
4185
4186
4187 setModified(IsModified_Storage);
4188 mMediaData.backup();
4189
4190 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4191
4192 if (pAttach->getType() != DeviceType_HardDisk)
4193 return setError(E_INVALIDARG,
4194 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"),
4195 aDevice, aControllerPort, aControllerName);
4196 pAttach->updateNonRotational(!!aNonRotational);
4197
4198 return S_OK;
4199}
4200
4201STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4202 LONG aDevice, BOOL aDiscard)
4203{
4204 CheckComArgStrNotEmptyOrNull(aControllerName);
4205
4206 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4207 aControllerName, aControllerPort, aDevice, aDiscard));
4208
4209 AutoCaller autoCaller(this);
4210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4211
4212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4213
4214 HRESULT rc = checkStateDependency(MutableStateDep);
4215 if (FAILED(rc)) return rc;
4216
4217 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4218
4219 if (Global::IsOnlineOrTransient(mData->mMachineState))
4220 return setError(VBOX_E_INVALID_VM_STATE,
4221 tr("Invalid machine state: %s"),
4222 Global::stringifyMachineState(mData->mMachineState));
4223
4224 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4225 aControllerName,
4226 aControllerPort,
4227 aDevice);
4228 if (!pAttach)
4229 return setError(VBOX_E_OBJECT_NOT_FOUND,
4230 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4231 aDevice, aControllerPort, aControllerName);
4232
4233
4234 setModified(IsModified_Storage);
4235 mMediaData.backup();
4236
4237 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4238
4239 if (pAttach->getType() != DeviceType_HardDisk)
4240 return setError(E_INVALIDARG,
4241 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"),
4242 aDevice, aControllerPort, aControllerName);
4243 pAttach->updateDiscard(!!aDiscard);
4244
4245 return S_OK;
4246}
4247
4248STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4249 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4250{
4251 CheckComArgStrNotEmptyOrNull(aControllerName);
4252
4253 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4254 aControllerName, aControllerPort, aDevice));
4255
4256 AutoCaller autoCaller(this);
4257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4258
4259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4260
4261 HRESULT rc = checkStateDependency(MutableStateDep);
4262 if (FAILED(rc)) return rc;
4263
4264 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4265
4266 if (Global::IsOnlineOrTransient(mData->mMachineState))
4267 return setError(VBOX_E_INVALID_VM_STATE,
4268 tr("Invalid machine state: %s"),
4269 Global::stringifyMachineState(mData->mMachineState));
4270
4271 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4272 aControllerName,
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4278 aDevice, aControllerPort, aControllerName);
4279
4280
4281 setModified(IsModified_Storage);
4282 mMediaData.backup();
4283
4284 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4285 if (aBandwidthGroup && group.isNull())
4286 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4287
4288 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4289
4290 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4291 if (strBandwidthGroupOld.isNotEmpty())
4292 {
4293 /* Get the bandwidth group object and release it - this must not fail. */
4294 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4295 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4296 Assert(SUCCEEDED(rc));
4297
4298 pBandwidthGroupOld->release();
4299 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4300 }
4301
4302 if (!group.isNull())
4303 {
4304 group->reference();
4305 pAttach->updateBandwidthGroup(group->getName());
4306 }
4307
4308 return S_OK;
4309}
4310
4311
4312STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4313 LONG aControllerPort,
4314 LONG aDevice,
4315 IMedium *aMedium,
4316 BOOL aForce)
4317{
4318 int rc = S_OK;
4319 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4320 aControllerName, aControllerPort, aDevice, aForce));
4321
4322 CheckComArgStrNotEmptyOrNull(aControllerName);
4323
4324 AutoCaller autoCaller(this);
4325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4326
4327 // request the host lock first, since might be calling Host methods for getting host drives;
4328 // next, protect the media tree all the while we're in here, as well as our member variables
4329 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4330 this->lockHandle(),
4331 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4332
4333 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4334 aControllerName,
4335 aControllerPort,
4336 aDevice);
4337 if (pAttach.isNull())
4338 return setError(VBOX_E_OBJECT_NOT_FOUND,
4339 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4340 aDevice, aControllerPort, aControllerName);
4341
4342 /* Remember previously mounted medium. The medium before taking the
4343 * backup is not necessarily the same thing. */
4344 ComObjPtr<Medium> oldmedium;
4345 oldmedium = pAttach->getMedium();
4346
4347 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4348 if (aMedium && pMedium.isNull())
4349 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4350
4351 AutoCaller mediumCaller(pMedium);
4352 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4353
4354 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4355 if (pMedium)
4356 {
4357 DeviceType_T mediumType = pAttach->getType();
4358 switch (mediumType)
4359 {
4360 case DeviceType_DVD:
4361 case DeviceType_Floppy:
4362 break;
4363
4364 default:
4365 return setError(VBOX_E_INVALID_OBJECT_STATE,
4366 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4367 aControllerPort,
4368 aDevice,
4369 aControllerName);
4370 }
4371 }
4372
4373 setModified(IsModified_Storage);
4374 mMediaData.backup();
4375
4376 {
4377 // The backup operation makes the pAttach reference point to the
4378 // old settings. Re-get the correct reference.
4379 pAttach = findAttachment(mMediaData->mAttachments,
4380 aControllerName,
4381 aControllerPort,
4382 aDevice);
4383 if (!oldmedium.isNull())
4384 oldmedium->removeBackReference(mData->mUuid);
4385 if (!pMedium.isNull())
4386 {
4387 pMedium->addBackReference(mData->mUuid);
4388
4389 mediumLock.release();
4390 multiLock.release();
4391 addMediumToRegistry(pMedium);
4392 multiLock.acquire();
4393 mediumLock.acquire();
4394 }
4395
4396 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4397 pAttach->updateMedium(pMedium);
4398 }
4399
4400 setModified(IsModified_Storage);
4401
4402 mediumLock.release();
4403 multiLock.release();
4404 rc = onMediumChange(pAttach, aForce);
4405 multiLock.acquire();
4406 mediumLock.acquire();
4407
4408 /* On error roll back this change only. */
4409 if (FAILED(rc))
4410 {
4411 if (!pMedium.isNull())
4412 pMedium->removeBackReference(mData->mUuid);
4413 pAttach = findAttachment(mMediaData->mAttachments,
4414 aControllerName,
4415 aControllerPort,
4416 aDevice);
4417 /* If the attachment is gone in the meantime, bail out. */
4418 if (pAttach.isNull())
4419 return rc;
4420 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4421 if (!oldmedium.isNull())
4422 oldmedium->addBackReference(mData->mUuid);
4423 pAttach->updateMedium(oldmedium);
4424 }
4425
4426 mediumLock.release();
4427 multiLock.release();
4428
4429 mParent->saveModifiedRegistries();
4430
4431 return rc;
4432}
4433
4434STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4435 LONG aControllerPort,
4436 LONG aDevice,
4437 IMedium **aMedium)
4438{
4439 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4440 aControllerName, aControllerPort, aDevice));
4441
4442 CheckComArgStrNotEmptyOrNull(aControllerName);
4443 CheckComArgOutPointerValid(aMedium);
4444
4445 AutoCaller autoCaller(this);
4446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4447
4448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4449
4450 *aMedium = NULL;
4451
4452 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4453 aControllerName,
4454 aControllerPort,
4455 aDevice);
4456 if (pAttach.isNull())
4457 return setError(VBOX_E_OBJECT_NOT_FOUND,
4458 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4459 aDevice, aControllerPort, aControllerName);
4460
4461 pAttach->getMedium().queryInterfaceTo(aMedium);
4462
4463 return S_OK;
4464}
4465
4466STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4467{
4468 CheckComArgOutPointerValid(port);
4469 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4470
4471 AutoCaller autoCaller(this);
4472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4473
4474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4475
4476 mSerialPorts[slot].queryInterfaceTo(port);
4477
4478 return S_OK;
4479}
4480
4481STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4482{
4483 CheckComArgOutPointerValid(port);
4484 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4485
4486 AutoCaller autoCaller(this);
4487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4488
4489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 mParallelPorts[slot].queryInterfaceTo(port);
4492
4493 return S_OK;
4494}
4495
4496STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4497{
4498 CheckComArgOutPointerValid(adapter);
4499 CheckComArgExpr(slot, slot < mNetworkAdapters.size());
4500
4501 AutoCaller autoCaller(this);
4502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4503
4504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4505
4506 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4507
4508 return S_OK;
4509}
4510
4511STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4512{
4513 CheckComArgOutSafeArrayPointerValid(aKeys);
4514
4515 AutoCaller autoCaller(this);
4516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4517
4518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4519
4520 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4521 int i = 0;
4522 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4523 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4524 ++it, ++i)
4525 {
4526 const Utf8Str &strKey = it->first;
4527 strKey.cloneTo(&saKeys[i]);
4528 }
4529 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4530
4531 return S_OK;
4532 }
4533
4534 /**
4535 * @note Locks this object for reading.
4536 */
4537STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4538 BSTR *aValue)
4539{
4540 CheckComArgStrNotEmptyOrNull(aKey);
4541 CheckComArgOutPointerValid(aValue);
4542
4543 AutoCaller autoCaller(this);
4544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4545
4546 /* start with nothing found */
4547 Bstr bstrResult("");
4548
4549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4550
4551 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4552 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4553 // found:
4554 bstrResult = it->second; // source is a Utf8Str
4555
4556 /* return the result to caller (may be empty) */
4557 bstrResult.cloneTo(aValue);
4558
4559 return S_OK;
4560}
4561
4562 /**
4563 * @note Locks mParent for writing + this object for writing.
4564 */
4565STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4566{
4567 CheckComArgStrNotEmptyOrNull(aKey);
4568
4569 AutoCaller autoCaller(this);
4570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4571
4572 Utf8Str strKey(aKey);
4573 Utf8Str strValue(aValue);
4574 Utf8Str strOldValue; // empty
4575
4576 // locking note: we only hold the read lock briefly to look up the old value,
4577 // then release it and call the onExtraCanChange callbacks. There is a small
4578 // chance of a race insofar as the callback might be called twice if two callers
4579 // change the same key at the same time, but that's a much better solution
4580 // than the deadlock we had here before. The actual changing of the extradata
4581 // is then performed under the write lock and race-free.
4582
4583 // look up the old value first; if nothing has changed then we need not do anything
4584 {
4585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4586 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4587 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4588 strOldValue = it->second;
4589 }
4590
4591 bool fChanged;
4592 if ((fChanged = (strOldValue != strValue)))
4593 {
4594 // ask for permission from all listeners outside the locks;
4595 // onExtraDataCanChange() only briefly requests the VirtualBox
4596 // lock to copy the list of callbacks to invoke
4597 Bstr error;
4598 Bstr bstrValue(aValue);
4599
4600 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4601 {
4602 const char *sep = error.isEmpty() ? "" : ": ";
4603 CBSTR err = error.raw();
4604 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4605 sep, err));
4606 return setError(E_ACCESSDENIED,
4607 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4608 aKey,
4609 bstrValue.raw(),
4610 sep,
4611 err);
4612 }
4613
4614 // data is changing and change not vetoed: then write it out under the lock
4615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 if (isSnapshotMachine())
4618 {
4619 HRESULT rc = checkStateDependency(MutableStateDep);
4620 if (FAILED(rc)) return rc;
4621 }
4622
4623 if (strValue.isEmpty())
4624 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4625 else
4626 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4627 // creates a new key if needed
4628
4629 bool fNeedsGlobalSaveSettings = false;
4630 saveSettings(&fNeedsGlobalSaveSettings);
4631
4632 if (fNeedsGlobalSaveSettings)
4633 {
4634 // save the global settings; for that we should hold only the VirtualBox lock
4635 alock.release();
4636 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4637 mParent->saveSettings();
4638 }
4639 }
4640
4641 // fire notification outside the lock
4642 if (fChanged)
4643 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4644
4645 return S_OK;
4646}
4647
4648STDMETHODIMP Machine::SaveSettings()
4649{
4650 AutoCaller autoCaller(this);
4651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4652
4653 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4654
4655 /* when there was auto-conversion, we want to save the file even if
4656 * the VM is saved */
4657 HRESULT rc = checkStateDependency(MutableStateDep);
4658 if (FAILED(rc)) return rc;
4659
4660 /* the settings file path may never be null */
4661 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4662
4663 /* save all VM data excluding snapshots */
4664 bool fNeedsGlobalSaveSettings = false;
4665 rc = saveSettings(&fNeedsGlobalSaveSettings);
4666 mlock.release();
4667
4668 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4669 {
4670 // save the global settings; for that we should hold only the VirtualBox lock
4671 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4672 rc = mParent->saveSettings();
4673 }
4674
4675 return rc;
4676}
4677
4678STDMETHODIMP Machine::DiscardSettings()
4679{
4680 AutoCaller autoCaller(this);
4681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4682
4683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4684
4685 HRESULT rc = checkStateDependency(MutableStateDep);
4686 if (FAILED(rc)) return rc;
4687
4688 /*
4689 * during this rollback, the session will be notified if data has
4690 * been actually changed
4691 */
4692 rollback(true /* aNotify */);
4693
4694 return S_OK;
4695}
4696
4697/** @note Locks objects! */
4698STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4699 ComSafeArrayOut(IMedium*, aMedia))
4700{
4701 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4702 AutoLimitedCaller autoCaller(this);
4703 AssertComRCReturnRC(autoCaller.rc());
4704
4705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4706
4707 Guid id(getId());
4708
4709 if (mData->mSession.mState != SessionState_Unlocked)
4710 return setError(VBOX_E_INVALID_OBJECT_STATE,
4711 tr("Cannot unregister the machine '%s' while it is locked"),
4712 mUserData->s.strName.c_str());
4713
4714 // wait for state dependents to drop to zero
4715 ensureNoStateDependencies();
4716
4717 if (!mData->mAccessible)
4718 {
4719 // inaccessible maschines can only be unregistered; uninitialize ourselves
4720 // here because currently there may be no unregistered that are inaccessible
4721 // (this state combination is not supported). Note releasing the caller and
4722 // leaving the lock before calling uninit()
4723 alock.release();
4724 autoCaller.release();
4725
4726 uninit();
4727
4728 mParent->unregisterMachine(this, id);
4729 // calls VirtualBox::saveSettings()
4730
4731 return S_OK;
4732 }
4733
4734 HRESULT rc = S_OK;
4735
4736 // discard saved state
4737 if (mData->mMachineState == MachineState_Saved)
4738 {
4739 // add the saved state file to the list of files the caller should delete
4740 Assert(!mSSData->strStateFilePath.isEmpty());
4741 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4742
4743 mSSData->strStateFilePath.setNull();
4744
4745 // unconditionally set the machine state to powered off, we now
4746 // know no session has locked the machine
4747 mData->mMachineState = MachineState_PoweredOff;
4748 }
4749
4750 size_t cSnapshots = 0;
4751 if (mData->mFirstSnapshot)
4752 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4753 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4754 // fail now before we start detaching media
4755 return setError(VBOX_E_INVALID_OBJECT_STATE,
4756 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4757 mUserData->s.strName.c_str(), cSnapshots);
4758
4759 // This list collects the medium objects from all medium attachments
4760 // which we will detach from the machine and its snapshots, in a specific
4761 // order which allows for closing all media without getting "media in use"
4762 // errors, simply by going through the list from the front to the back:
4763 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4764 // and must be closed before the parent media from the snapshots, or closing the parents
4765 // will fail because they still have children);
4766 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4767 // the root ("first") snapshot of the machine.
4768 MediaList llMedia;
4769
4770 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4771 && mMediaData->mAttachments.size()
4772 )
4773 {
4774 // we have media attachments: detach them all and add the Medium objects to our list
4775 if (cleanupMode != CleanupMode_UnregisterOnly)
4776 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4777 else
4778 return setError(VBOX_E_INVALID_OBJECT_STATE,
4779 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4780 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4781 }
4782
4783 if (cSnapshots)
4784 {
4785 // autoCleanup must be true here, or we would have failed above
4786
4787 // add the media from the medium attachments of the snapshots to llMedia
4788 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4789 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4790 // into the children first
4791
4792 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4793 MachineState_T oldState = mData->mMachineState;
4794 mData->mMachineState = MachineState_DeletingSnapshot;
4795
4796 // make a copy of the first snapshot so the refcount does not drop to 0
4797 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4798 // because of the AutoCaller voodoo)
4799 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4800
4801 // GO!
4802 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4803
4804 mData->mMachineState = oldState;
4805 }
4806
4807 if (FAILED(rc))
4808 {
4809 rollbackMedia();
4810 return rc;
4811 }
4812
4813 // commit all the media changes made above
4814 commitMedia();
4815
4816 mData->mRegistered = false;
4817
4818 // machine lock no longer needed
4819 alock.release();
4820
4821 // return media to caller
4822 SafeIfaceArray<IMedium> sfaMedia(llMedia);
4823 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4824
4825 mParent->unregisterMachine(this, id);
4826 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
4827
4828 return S_OK;
4829}
4830
4831struct Machine::DeleteTask
4832{
4833 ComObjPtr<Machine> pMachine;
4834 RTCList<ComPtr<IMedium> > llMediums;
4835 StringsList llFilesToDelete;
4836 ComObjPtr<Progress> pProgress;
4837};
4838
4839STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4840{
4841 LogFlowFuncEnter();
4842
4843 AutoCaller autoCaller(this);
4844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4845
4846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4847
4848 HRESULT rc = checkStateDependency(MutableStateDep);
4849 if (FAILED(rc)) return rc;
4850
4851 if (mData->mRegistered)
4852 return setError(VBOX_E_INVALID_VM_STATE,
4853 tr("Cannot delete settings of a registered machine"));
4854
4855 DeleteTask *pTask = new DeleteTask;
4856 pTask->pMachine = this;
4857 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4858
4859 // collect files to delete
4860 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4861
4862 for (size_t i = 0; i < sfaMedia.size(); ++i)
4863 {
4864 IMedium *pIMedium(sfaMedia[i]);
4865 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4866 if (pMedium.isNull())
4867 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
4868 SafeArray<BSTR> ids;
4869 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4870 if (FAILED(rc)) return rc;
4871 /* At this point the medium should not have any back references
4872 * anymore. If it has it is attached to another VM and *must* not
4873 * deleted. */
4874 if (ids.size() < 1)
4875 pTask->llMediums.append(pMedium);
4876 }
4877 if (mData->pMachineConfigFile->fileExists())
4878 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4879
4880 pTask->pProgress.createObject();
4881 pTask->pProgress->init(getVirtualBox(),
4882 static_cast<IMachine*>(this) /* aInitiator */,
4883 Bstr(tr("Deleting files")).raw(),
4884 true /* fCancellable */,
4885 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
4886 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4887
4888 int vrc = RTThreadCreate(NULL,
4889 Machine::deleteThread,
4890 (void*)pTask,
4891 0,
4892 RTTHREADTYPE_MAIN_WORKER,
4893 0,
4894 "MachineDelete");
4895
4896 pTask->pProgress.queryInterfaceTo(aProgress);
4897
4898 if (RT_FAILURE(vrc))
4899 {
4900 delete pTask;
4901 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4902 }
4903
4904 LogFlowFuncLeave();
4905
4906 return S_OK;
4907}
4908
4909/**
4910 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4911 * calls Machine::deleteTaskWorker() on the actual machine object.
4912 * @param Thread
4913 * @param pvUser
4914 * @return
4915 */
4916/*static*/
4917DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
4918{
4919 LogFlowFuncEnter();
4920
4921 DeleteTask *pTask = (DeleteTask*)pvUser;
4922 Assert(pTask);
4923 Assert(pTask->pMachine);
4924 Assert(pTask->pProgress);
4925
4926 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
4927 pTask->pProgress->notifyComplete(rc);
4928
4929 delete pTask;
4930
4931 LogFlowFuncLeave();
4932
4933 NOREF(Thread);
4934
4935 return VINF_SUCCESS;
4936}
4937
4938/**
4939 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
4940 * @param task
4941 * @return
4942 */
4943HRESULT Machine::deleteTaskWorker(DeleteTask &task)
4944{
4945 AutoCaller autoCaller(this);
4946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4947
4948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4949
4950 HRESULT rc = S_OK;
4951
4952 try
4953 {
4954 ULONG uLogHistoryCount = 3;
4955 ComPtr<ISystemProperties> systemProperties;
4956 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4957 if (FAILED(rc)) throw rc;
4958
4959 if (!systemProperties.isNull())
4960 {
4961 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4962 if (FAILED(rc)) throw rc;
4963 }
4964
4965 MachineState_T oldState = mData->mMachineState;
4966 setMachineState(MachineState_SettingUp);
4967 alock.release();
4968 for (size_t i = 0; i < task.llMediums.size(); ++i)
4969 {
4970 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
4971 {
4972 AutoCaller mac(pMedium);
4973 if (FAILED(mac.rc())) throw mac.rc();
4974 Utf8Str strLocation = pMedium->getLocationFull();
4975 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4976 if (FAILED(rc)) throw rc;
4977 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4978 }
4979 ComPtr<IProgress> pProgress2;
4980 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
4981 if (FAILED(rc)) throw rc;
4982 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
4983 if (FAILED(rc)) throw rc;
4984 /* Check the result of the asynchrony process. */
4985 LONG iRc;
4986 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
4987 if (FAILED(rc)) throw rc;
4988 /* If the thread of the progress object has an error, then
4989 * retrieve the error info from there, or it'll be lost. */
4990 if (FAILED(iRc))
4991 throw setError(ProgressErrorInfo(pProgress2));
4992 }
4993 setMachineState(oldState);
4994 alock.acquire();
4995
4996 // delete the files pushed on the task list by Machine::Delete()
4997 // (this includes saved states of the machine and snapshots and
4998 // medium storage files from the IMedium list passed in, and the
4999 // machine XML file)
5000 StringsList::const_iterator it = task.llFilesToDelete.begin();
5001 while (it != task.llFilesToDelete.end())
5002 {
5003 const Utf8Str &strFile = *it;
5004 LogFunc(("Deleting file %s\n", strFile.c_str()));
5005 int vrc = RTFileDelete(strFile.c_str());
5006 if (RT_FAILURE(vrc))
5007 throw setError(VBOX_E_IPRT_ERROR,
5008 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5009
5010 ++it;
5011 if (it == task.llFilesToDelete.end())
5012 {
5013 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5014 if (FAILED(rc)) throw rc;
5015 break;
5016 }
5017
5018 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5019 if (FAILED(rc)) throw rc;
5020 }
5021
5022 /* delete the settings only when the file actually exists */
5023 if (mData->pMachineConfigFile->fileExists())
5024 {
5025 /* Delete any backup or uncommitted XML files. Ignore failures.
5026 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5027 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5028 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5029 RTFileDelete(otherXml.c_str());
5030 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5031 RTFileDelete(otherXml.c_str());
5032
5033 /* delete the Logs folder, nothing important should be left
5034 * there (we don't check for errors because the user might have
5035 * some private files there that we don't want to delete) */
5036 Utf8Str logFolder;
5037 getLogFolder(logFolder);
5038 Assert(logFolder.length());
5039 if (RTDirExists(logFolder.c_str()))
5040 {
5041 /* Delete all VBox.log[.N] files from the Logs folder
5042 * (this must be in sync with the rotation logic in
5043 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5044 * files that may have been created by the GUI. */
5045 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5046 logFolder.c_str(), RTPATH_DELIMITER);
5047 RTFileDelete(log.c_str());
5048 log = Utf8StrFmt("%s%cVBox.png",
5049 logFolder.c_str(), RTPATH_DELIMITER);
5050 RTFileDelete(log.c_str());
5051 for (int i = uLogHistoryCount; i > 0; i--)
5052 {
5053 log = Utf8StrFmt("%s%cVBox.log.%d",
5054 logFolder.c_str(), RTPATH_DELIMITER, i);
5055 RTFileDelete(log.c_str());
5056 log = Utf8StrFmt("%s%cVBox.png.%d",
5057 logFolder.c_str(), RTPATH_DELIMITER, i);
5058 RTFileDelete(log.c_str());
5059 }
5060
5061 RTDirRemove(logFolder.c_str());
5062 }
5063
5064 /* delete the Snapshots folder, nothing important should be left
5065 * there (we don't check for errors because the user might have
5066 * some private files there that we don't want to delete) */
5067 Utf8Str strFullSnapshotFolder;
5068 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5069 Assert(!strFullSnapshotFolder.isEmpty());
5070 if (RTDirExists(strFullSnapshotFolder.c_str()))
5071 RTDirRemove(strFullSnapshotFolder.c_str());
5072
5073 // delete the directory that contains the settings file, but only
5074 // if it matches the VM name
5075 Utf8Str settingsDir;
5076 if (isInOwnDir(&settingsDir))
5077 RTDirRemove(settingsDir.c_str());
5078 }
5079
5080 alock.release();
5081
5082 mParent->saveModifiedRegistries();
5083 }
5084 catch (HRESULT aRC) { rc = aRC; }
5085
5086 return rc;
5087}
5088
5089STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5090{
5091 CheckComArgOutPointerValid(aSnapshot);
5092
5093 AutoCaller autoCaller(this);
5094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5095
5096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5097
5098 ComObjPtr<Snapshot> pSnapshot;
5099 HRESULT rc;
5100
5101 if (!aNameOrId || !*aNameOrId)
5102 // null case (caller wants root snapshot): findSnapshotById() handles this
5103 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5104 else
5105 {
5106 Guid uuid(aNameOrId);
5107 if (!uuid.isEmpty())
5108 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5109 else
5110 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5111 }
5112 pSnapshot.queryInterfaceTo(aSnapshot);
5113
5114 return rc;
5115}
5116
5117STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5118{
5119 CheckComArgStrNotEmptyOrNull(aName);
5120 CheckComArgStrNotEmptyOrNull(aHostPath);
5121
5122 AutoCaller autoCaller(this);
5123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5124
5125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5126
5127 HRESULT rc = checkStateDependency(MutableStateDep);
5128 if (FAILED(rc)) return rc;
5129
5130 Utf8Str strName(aName);
5131
5132 ComObjPtr<SharedFolder> sharedFolder;
5133 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5134 if (SUCCEEDED(rc))
5135 return setError(VBOX_E_OBJECT_IN_USE,
5136 tr("Shared folder named '%s' already exists"),
5137 strName.c_str());
5138
5139 sharedFolder.createObject();
5140 rc = sharedFolder->init(getMachine(),
5141 strName,
5142 aHostPath,
5143 !!aWritable,
5144 !!aAutoMount,
5145 true /* fFailOnError */);
5146 if (FAILED(rc)) return rc;
5147
5148 setModified(IsModified_SharedFolders);
5149 mHWData.backup();
5150 mHWData->mSharedFolders.push_back(sharedFolder);
5151
5152 /* inform the direct session if any */
5153 alock.release();
5154 onSharedFolderChange();
5155
5156 return S_OK;
5157}
5158
5159STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5160{
5161 CheckComArgStrNotEmptyOrNull(aName);
5162
5163 AutoCaller autoCaller(this);
5164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5165
5166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5167
5168 HRESULT rc = checkStateDependency(MutableStateDep);
5169 if (FAILED(rc)) return rc;
5170
5171 ComObjPtr<SharedFolder> sharedFolder;
5172 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5173 if (FAILED(rc)) return rc;
5174
5175 setModified(IsModified_SharedFolders);
5176 mHWData.backup();
5177 mHWData->mSharedFolders.remove(sharedFolder);
5178
5179 /* inform the direct session if any */
5180 alock.release();
5181 onSharedFolderChange();
5182
5183 return S_OK;
5184}
5185
5186STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5187{
5188 CheckComArgOutPointerValid(aCanShow);
5189
5190 /* start with No */
5191 *aCanShow = FALSE;
5192
5193 AutoCaller autoCaller(this);
5194 AssertComRCReturnRC(autoCaller.rc());
5195
5196 ComPtr<IInternalSessionControl> directControl;
5197 {
5198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5199
5200 if (mData->mSession.mState != SessionState_Locked)
5201 return setError(VBOX_E_INVALID_VM_STATE,
5202 tr("Machine is not locked for session (session state: %s)"),
5203 Global::stringifySessionState(mData->mSession.mState));
5204
5205 directControl = mData->mSession.mDirectControl;
5206 }
5207
5208 /* ignore calls made after #OnSessionEnd() is called */
5209 if (!directControl)
5210 return S_OK;
5211
5212 LONG64 dummy;
5213 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5214}
5215
5216STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5217{
5218 CheckComArgOutPointerValid(aWinId);
5219
5220 AutoCaller autoCaller(this);
5221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5222
5223 ComPtr<IInternalSessionControl> directControl;
5224 {
5225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5226
5227 if (mData->mSession.mState != SessionState_Locked)
5228 return setError(E_FAIL,
5229 tr("Machine is not locked for session (session state: %s)"),
5230 Global::stringifySessionState(mData->mSession.mState));
5231
5232 directControl = mData->mSession.mDirectControl;
5233 }
5234
5235 /* ignore calls made after #OnSessionEnd() is called */
5236 if (!directControl)
5237 return S_OK;
5238
5239 BOOL dummy;
5240 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5241}
5242
5243#ifdef VBOX_WITH_GUEST_PROPS
5244/**
5245 * Look up a guest property in VBoxSVC's internal structures.
5246 */
5247HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5248 BSTR *aValue,
5249 LONG64 *aTimestamp,
5250 BSTR *aFlags) const
5251{
5252 using namespace guestProp;
5253
5254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5255 Utf8Str strName(aName);
5256 HWData::GuestPropertyList::const_iterator it;
5257
5258 for (it = mHWData->mGuestProperties.begin();
5259 it != mHWData->mGuestProperties.end(); ++it)
5260 {
5261 if (it->strName == strName)
5262 {
5263 char szFlags[MAX_FLAGS_LEN + 1];
5264 it->strValue.cloneTo(aValue);
5265 *aTimestamp = it->mTimestamp;
5266 writeFlags(it->mFlags, szFlags);
5267 Bstr(szFlags).cloneTo(aFlags);
5268 break;
5269 }
5270 }
5271 return S_OK;
5272}
5273
5274/**
5275 * Query the VM that a guest property belongs to for the property.
5276 * @returns E_ACCESSDENIED if the VM process is not available or not
5277 * currently handling queries and the lookup should then be done in
5278 * VBoxSVC.
5279 */
5280HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5281 BSTR *aValue,
5282 LONG64 *aTimestamp,
5283 BSTR *aFlags) const
5284{
5285 HRESULT rc;
5286 ComPtr<IInternalSessionControl> directControl;
5287 directControl = mData->mSession.mDirectControl;
5288
5289 /* fail if we were called after #OnSessionEnd() is called. This is a
5290 * silly race condition. */
5291
5292 if (!directControl)
5293 rc = E_ACCESSDENIED;
5294 else
5295 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5296 false /* isSetter */,
5297 aValue, aTimestamp, aFlags);
5298 return rc;
5299}
5300#endif // VBOX_WITH_GUEST_PROPS
5301
5302STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5303 BSTR *aValue,
5304 LONG64 *aTimestamp,
5305 BSTR *aFlags)
5306{
5307#ifndef VBOX_WITH_GUEST_PROPS
5308 ReturnComNotImplemented();
5309#else // VBOX_WITH_GUEST_PROPS
5310 CheckComArgStrNotEmptyOrNull(aName);
5311 CheckComArgOutPointerValid(aValue);
5312 CheckComArgOutPointerValid(aTimestamp);
5313 CheckComArgOutPointerValid(aFlags);
5314
5315 AutoCaller autoCaller(this);
5316 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5317
5318 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5319 if (rc == E_ACCESSDENIED)
5320 /* The VM is not running or the service is not (yet) accessible */
5321 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5322 return rc;
5323#endif // VBOX_WITH_GUEST_PROPS
5324}
5325
5326STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5327{
5328 LONG64 dummyTimestamp;
5329 Bstr dummyFlags;
5330 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5331}
5332
5333STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5334{
5335 Bstr dummyValue;
5336 Bstr dummyFlags;
5337 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5338}
5339
5340#ifdef VBOX_WITH_GUEST_PROPS
5341/**
5342 * Set a guest property in VBoxSVC's internal structures.
5343 */
5344HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5345 IN_BSTR aFlags)
5346{
5347 using namespace guestProp;
5348
5349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5350 HRESULT rc = S_OK;
5351 HWData::GuestProperty property;
5352 property.mFlags = NILFLAG;
5353 bool found = false;
5354
5355 rc = checkStateDependency(MutableStateDep);
5356 if (FAILED(rc)) return rc;
5357
5358 try
5359 {
5360 Utf8Str utf8Name(aName);
5361 Utf8Str utf8Flags(aFlags);
5362 uint32_t fFlags = NILFLAG;
5363 if ( (aFlags != NULL)
5364 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5365 )
5366 return setError(E_INVALIDARG,
5367 tr("Invalid flag values: '%ls'"),
5368 aFlags);
5369
5370 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5371 * know, this is simple and do an OK job atm.) */
5372 HWData::GuestPropertyList::iterator it;
5373 for (it = mHWData->mGuestProperties.begin();
5374 it != mHWData->mGuestProperties.end(); ++it)
5375 if (it->strName == utf8Name)
5376 {
5377 property = *it;
5378 if (it->mFlags & (RDONLYHOST))
5379 rc = setError(E_ACCESSDENIED,
5380 tr("The property '%ls' cannot be changed by the host"),
5381 aName);
5382 else
5383 {
5384 setModified(IsModified_MachineData);
5385 mHWData.backup(); // @todo r=dj backup in a loop?!?
5386
5387 /* The backup() operation invalidates our iterator, so
5388 * get a new one. */
5389 for (it = mHWData->mGuestProperties.begin();
5390 it->strName != utf8Name;
5391 ++it)
5392 ;
5393 mHWData->mGuestProperties.erase(it);
5394 }
5395 found = true;
5396 break;
5397 }
5398 if (found && SUCCEEDED(rc))
5399 {
5400 if (aValue)
5401 {
5402 RTTIMESPEC time;
5403 property.strValue = aValue;
5404 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5405 if (aFlags != NULL)
5406 property.mFlags = fFlags;
5407 mHWData->mGuestProperties.push_back(property);
5408 }
5409 }
5410 else if (SUCCEEDED(rc) && aValue)
5411 {
5412 RTTIMESPEC time;
5413 setModified(IsModified_MachineData);
5414 mHWData.backup();
5415 property.strName = aName;
5416 property.strValue = aValue;
5417 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5418 property.mFlags = fFlags;
5419 mHWData->mGuestProperties.push_back(property);
5420 }
5421 if ( SUCCEEDED(rc)
5422 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5423 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5424 RTSTR_MAX,
5425 utf8Name.c_str(),
5426 RTSTR_MAX,
5427 NULL)
5428 )
5429 )
5430 {
5431 /** @todo r=bird: Why aren't we leaving the lock here? The
5432 * same code in PushGuestProperty does... */
5433 mParent->onGuestPropertyChange(mData->mUuid, aName,
5434 aValue ? aValue : Bstr("").raw(),
5435 aFlags ? aFlags : Bstr("").raw());
5436 }
5437 }
5438 catch (std::bad_alloc &)
5439 {
5440 rc = E_OUTOFMEMORY;
5441 }
5442
5443 return rc;
5444}
5445
5446/**
5447 * Set a property on the VM that that property belongs to.
5448 * @returns E_ACCESSDENIED if the VM process is not available or not
5449 * currently handling queries and the setting should then be done in
5450 * VBoxSVC.
5451 */
5452HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5453 IN_BSTR aFlags)
5454{
5455 HRESULT rc;
5456
5457 try
5458 {
5459 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5460
5461 BSTR dummy = NULL; /* will not be changed (setter) */
5462 LONG64 dummy64;
5463 if (!directControl)
5464 rc = E_ACCESSDENIED;
5465 else
5466 /** @todo Fix when adding DeleteGuestProperty(),
5467 see defect. */
5468 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5469 true /* isSetter */,
5470 &dummy, &dummy64, &dummy);
5471 }
5472 catch (std::bad_alloc &)
5473 {
5474 rc = E_OUTOFMEMORY;
5475 }
5476
5477 return rc;
5478}
5479#endif // VBOX_WITH_GUEST_PROPS
5480
5481STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5482 IN_BSTR aFlags)
5483{
5484#ifndef VBOX_WITH_GUEST_PROPS
5485 ReturnComNotImplemented();
5486#else // VBOX_WITH_GUEST_PROPS
5487 CheckComArgStrNotEmptyOrNull(aName);
5488 CheckComArgMaybeNull(aFlags);
5489 CheckComArgMaybeNull(aValue);
5490
5491 AutoCaller autoCaller(this);
5492 if (FAILED(autoCaller.rc()))
5493 return autoCaller.rc();
5494
5495 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5496 if (rc == E_ACCESSDENIED)
5497 /* The VM is not running or the service is not (yet) accessible */
5498 rc = setGuestPropertyToService(aName, aValue, aFlags);
5499 return rc;
5500#endif // VBOX_WITH_GUEST_PROPS
5501}
5502
5503STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5504{
5505 return SetGuestProperty(aName, aValue, NULL);
5506}
5507
5508STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5509{
5510 return SetGuestProperty(aName, NULL, NULL);
5511}
5512
5513#ifdef VBOX_WITH_GUEST_PROPS
5514/**
5515 * Enumerate the guest properties in VBoxSVC's internal structures.
5516 */
5517HRESULT Machine::enumerateGuestPropertiesInService
5518 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5519 ComSafeArrayOut(BSTR, aValues),
5520 ComSafeArrayOut(LONG64, aTimestamps),
5521 ComSafeArrayOut(BSTR, aFlags))
5522{
5523 using namespace guestProp;
5524
5525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5526 Utf8Str strPatterns(aPatterns);
5527
5528 /*
5529 * Look for matching patterns and build up a list.
5530 */
5531 HWData::GuestPropertyList propList;
5532 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5533 it != mHWData->mGuestProperties.end();
5534 ++it)
5535 if ( strPatterns.isEmpty()
5536 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5537 RTSTR_MAX,
5538 it->strName.c_str(),
5539 RTSTR_MAX,
5540 NULL)
5541 )
5542 propList.push_back(*it);
5543
5544 /*
5545 * And build up the arrays for returning the property information.
5546 */
5547 size_t cEntries = propList.size();
5548 SafeArray<BSTR> names(cEntries);
5549 SafeArray<BSTR> values(cEntries);
5550 SafeArray<LONG64> timestamps(cEntries);
5551 SafeArray<BSTR> flags(cEntries);
5552 size_t iProp = 0;
5553 for (HWData::GuestPropertyList::iterator it = propList.begin();
5554 it != propList.end();
5555 ++it)
5556 {
5557 char szFlags[MAX_FLAGS_LEN + 1];
5558 it->strName.cloneTo(&names[iProp]);
5559 it->strValue.cloneTo(&values[iProp]);
5560 timestamps[iProp] = it->mTimestamp;
5561 writeFlags(it->mFlags, szFlags);
5562 Bstr(szFlags).cloneTo(&flags[iProp]);
5563 ++iProp;
5564 }
5565 names.detachTo(ComSafeArrayOutArg(aNames));
5566 values.detachTo(ComSafeArrayOutArg(aValues));
5567 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5568 flags.detachTo(ComSafeArrayOutArg(aFlags));
5569 return S_OK;
5570}
5571
5572/**
5573 * Enumerate the properties managed by a VM.
5574 * @returns E_ACCESSDENIED if the VM process is not available or not
5575 * currently handling queries and the setting should then be done in
5576 * VBoxSVC.
5577 */
5578HRESULT Machine::enumerateGuestPropertiesOnVM
5579 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5580 ComSafeArrayOut(BSTR, aValues),
5581 ComSafeArrayOut(LONG64, aTimestamps),
5582 ComSafeArrayOut(BSTR, aFlags))
5583{
5584 HRESULT rc;
5585 ComPtr<IInternalSessionControl> directControl;
5586 directControl = mData->mSession.mDirectControl;
5587
5588 if (!directControl)
5589 rc = E_ACCESSDENIED;
5590 else
5591 rc = directControl->EnumerateGuestProperties
5592 (aPatterns, ComSafeArrayOutArg(aNames),
5593 ComSafeArrayOutArg(aValues),
5594 ComSafeArrayOutArg(aTimestamps),
5595 ComSafeArrayOutArg(aFlags));
5596 return rc;
5597}
5598#endif // VBOX_WITH_GUEST_PROPS
5599
5600STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5601 ComSafeArrayOut(BSTR, aNames),
5602 ComSafeArrayOut(BSTR, aValues),
5603 ComSafeArrayOut(LONG64, aTimestamps),
5604 ComSafeArrayOut(BSTR, aFlags))
5605{
5606#ifndef VBOX_WITH_GUEST_PROPS
5607 ReturnComNotImplemented();
5608#else // VBOX_WITH_GUEST_PROPS
5609 CheckComArgMaybeNull(aPatterns);
5610 CheckComArgOutSafeArrayPointerValid(aNames);
5611 CheckComArgOutSafeArrayPointerValid(aValues);
5612 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5613 CheckComArgOutSafeArrayPointerValid(aFlags);
5614
5615 AutoCaller autoCaller(this);
5616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5617
5618 HRESULT rc = enumerateGuestPropertiesOnVM
5619 (aPatterns, ComSafeArrayOutArg(aNames),
5620 ComSafeArrayOutArg(aValues),
5621 ComSafeArrayOutArg(aTimestamps),
5622 ComSafeArrayOutArg(aFlags));
5623 if (rc == E_ACCESSDENIED)
5624 /* The VM is not running or the service is not (yet) accessible */
5625 rc = enumerateGuestPropertiesInService
5626 (aPatterns, ComSafeArrayOutArg(aNames),
5627 ComSafeArrayOutArg(aValues),
5628 ComSafeArrayOutArg(aTimestamps),
5629 ComSafeArrayOutArg(aFlags));
5630 return rc;
5631#endif // VBOX_WITH_GUEST_PROPS
5632}
5633
5634STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5635 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5636{
5637 MediaData::AttachmentList atts;
5638
5639 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5640 if (FAILED(rc)) return rc;
5641
5642 SafeIfaceArray<IMediumAttachment> attachments(atts);
5643 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5644
5645 return S_OK;
5646}
5647
5648STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5649 LONG aControllerPort,
5650 LONG aDevice,
5651 IMediumAttachment **aAttachment)
5652{
5653 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5654 aControllerName, aControllerPort, aDevice));
5655
5656 CheckComArgStrNotEmptyOrNull(aControllerName);
5657 CheckComArgOutPointerValid(aAttachment);
5658
5659 AutoCaller autoCaller(this);
5660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5661
5662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5663
5664 *aAttachment = NULL;
5665
5666 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5667 aControllerName,
5668 aControllerPort,
5669 aDevice);
5670 if (pAttach.isNull())
5671 return setError(VBOX_E_OBJECT_NOT_FOUND,
5672 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5673 aDevice, aControllerPort, aControllerName);
5674
5675 pAttach.queryInterfaceTo(aAttachment);
5676
5677 return S_OK;
5678}
5679
5680STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5681 StorageBus_T aConnectionType,
5682 IStorageController **controller)
5683{
5684 CheckComArgStrNotEmptyOrNull(aName);
5685
5686 if ( (aConnectionType <= StorageBus_Null)
5687 || (aConnectionType > StorageBus_SAS))
5688 return setError(E_INVALIDARG,
5689 tr("Invalid connection type: %d"),
5690 aConnectionType);
5691
5692 AutoCaller autoCaller(this);
5693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5694
5695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5696
5697 HRESULT rc = checkStateDependency(MutableStateDep);
5698 if (FAILED(rc)) return rc;
5699
5700 /* try to find one with the name first. */
5701 ComObjPtr<StorageController> ctrl;
5702
5703 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5704 if (SUCCEEDED(rc))
5705 return setError(VBOX_E_OBJECT_IN_USE,
5706 tr("Storage controller named '%ls' already exists"),
5707 aName);
5708
5709 ctrl.createObject();
5710
5711 /* get a new instance number for the storage controller */
5712 ULONG ulInstance = 0;
5713 bool fBootable = true;
5714 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5715 it != mStorageControllers->end();
5716 ++it)
5717 {
5718 if ((*it)->getStorageBus() == aConnectionType)
5719 {
5720 ULONG ulCurInst = (*it)->getInstance();
5721
5722 if (ulCurInst >= ulInstance)
5723 ulInstance = ulCurInst + 1;
5724
5725 /* Only one controller of each type can be marked as bootable. */
5726 if ((*it)->getBootable())
5727 fBootable = false;
5728 }
5729 }
5730
5731 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5732 if (FAILED(rc)) return rc;
5733
5734 setModified(IsModified_Storage);
5735 mStorageControllers.backup();
5736 mStorageControllers->push_back(ctrl);
5737
5738 ctrl.queryInterfaceTo(controller);
5739
5740 /* inform the direct session if any */
5741 alock.release();
5742 onStorageControllerChange();
5743
5744 return S_OK;
5745}
5746
5747STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5748 IStorageController **aStorageController)
5749{
5750 CheckComArgStrNotEmptyOrNull(aName);
5751
5752 AutoCaller autoCaller(this);
5753 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5754
5755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5756
5757 ComObjPtr<StorageController> ctrl;
5758
5759 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5760 if (SUCCEEDED(rc))
5761 ctrl.queryInterfaceTo(aStorageController);
5762
5763 return rc;
5764}
5765
5766STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5767 IStorageController **aStorageController)
5768{
5769 AutoCaller autoCaller(this);
5770 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5771
5772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5773
5774 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5775 it != mStorageControllers->end();
5776 ++it)
5777 {
5778 if ((*it)->getInstance() == aInstance)
5779 {
5780 (*it).queryInterfaceTo(aStorageController);
5781 return S_OK;
5782 }
5783 }
5784
5785 return setError(VBOX_E_OBJECT_NOT_FOUND,
5786 tr("Could not find a storage controller with instance number '%lu'"),
5787 aInstance);
5788}
5789
5790STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5791{
5792 AutoCaller autoCaller(this);
5793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5794
5795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5796
5797 HRESULT rc = checkStateDependency(MutableStateDep);
5798 if (FAILED(rc)) return rc;
5799
5800 ComObjPtr<StorageController> ctrl;
5801
5802 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5803 if (SUCCEEDED(rc))
5804 {
5805 /* Ensure that only one controller of each type is marked as bootable. */
5806 if (fBootable == TRUE)
5807 {
5808 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5809 it != mStorageControllers->end();
5810 ++it)
5811 {
5812 ComObjPtr<StorageController> aCtrl = (*it);
5813
5814 if ( (aCtrl->getName() != Utf8Str(aName))
5815 && aCtrl->getBootable() == TRUE
5816 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5817 && aCtrl->getControllerType() == ctrl->getControllerType())
5818 {
5819 aCtrl->setBootable(FALSE);
5820 break;
5821 }
5822 }
5823 }
5824
5825 if (SUCCEEDED(rc))
5826 {
5827 ctrl->setBootable(fBootable);
5828 setModified(IsModified_Storage);
5829 }
5830 }
5831
5832 if (SUCCEEDED(rc))
5833 {
5834 /* inform the direct session if any */
5835 alock.release();
5836 onStorageControllerChange();
5837 }
5838
5839 return rc;
5840}
5841
5842STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5843{
5844 CheckComArgStrNotEmptyOrNull(aName);
5845
5846 AutoCaller autoCaller(this);
5847 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5848
5849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5850
5851 HRESULT rc = checkStateDependency(MutableStateDep);
5852 if (FAILED(rc)) return rc;
5853
5854 ComObjPtr<StorageController> ctrl;
5855 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5856 if (FAILED(rc)) return rc;
5857
5858 {
5859 /* find all attached devices to the appropriate storage controller and detach them all*/
5860 MediaData::AttachmentList::const_iterator endList = mMediaData->mAttachments.end();
5861 MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5862 for (;it != endList; it++)
5863 {
5864 MediumAttachment *pAttachTemp = *it;
5865 AutoCaller localAutoCaller(pAttachTemp);
5866 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5867
5868 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5869
5870 if (pAttachTemp->getControllerName() == aName)
5871 {
5872 LONG port = pAttachTemp->getPort();
5873 LONG device = pAttachTemp->getDevice();
5874 rc = DetachDevice(aName, port, device);
5875 if (FAILED(rc)) return rc;
5876 }
5877 }
5878 }
5879
5880 /* We can remove it now. */
5881 setModified(IsModified_Storage);
5882 mStorageControllers.backup();
5883
5884 ctrl->unshare();
5885
5886 mStorageControllers->remove(ctrl);
5887
5888 /* inform the direct session if any */
5889 alock.release();
5890 onStorageControllerChange();
5891
5892 return S_OK;
5893}
5894
5895STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5896 ULONG *puOriginX,
5897 ULONG *puOriginY,
5898 ULONG *puWidth,
5899 ULONG *puHeight,
5900 BOOL *pfEnabled)
5901{
5902 LogFlowThisFunc(("\n"));
5903
5904 CheckComArgNotNull(puOriginX);
5905 CheckComArgNotNull(puOriginY);
5906 CheckComArgNotNull(puWidth);
5907 CheckComArgNotNull(puHeight);
5908 CheckComArgNotNull(pfEnabled);
5909
5910 uint32_t u32OriginX= 0;
5911 uint32_t u32OriginY= 0;
5912 uint32_t u32Width = 0;
5913 uint32_t u32Height = 0;
5914 uint16_t u16Flags = 0;
5915
5916 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
5917 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5918 if (RT_FAILURE(vrc))
5919 {
5920#ifdef RT_OS_WINDOWS
5921 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5922 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5923 * So just assign fEnable to TRUE again.
5924 * The right fix would be to change GUI API wrappers to make sure that parameters
5925 * are changed only if API succeeds.
5926 */
5927 *pfEnabled = TRUE;
5928#endif
5929 return setError(VBOX_E_IPRT_ERROR,
5930 tr("Saved guest size is not available (%Rrc)"),
5931 vrc);
5932 }
5933
5934 *puOriginX = u32OriginX;
5935 *puOriginY = u32OriginY;
5936 *puWidth = u32Width;
5937 *puHeight = u32Height;
5938 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5939
5940 return S_OK;
5941}
5942
5943STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5944{
5945 LogFlowThisFunc(("\n"));
5946
5947 CheckComArgNotNull(aSize);
5948 CheckComArgNotNull(aWidth);
5949 CheckComArgNotNull(aHeight);
5950
5951 if (aScreenId != 0)
5952 return E_NOTIMPL;
5953
5954 AutoCaller autoCaller(this);
5955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5956
5957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5958
5959 uint8_t *pu8Data = NULL;
5960 uint32_t cbData = 0;
5961 uint32_t u32Width = 0;
5962 uint32_t u32Height = 0;
5963
5964 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5965
5966 if (RT_FAILURE(vrc))
5967 return setError(VBOX_E_IPRT_ERROR,
5968 tr("Saved screenshot data is not available (%Rrc)"),
5969 vrc);
5970
5971 *aSize = cbData;
5972 *aWidth = u32Width;
5973 *aHeight = u32Height;
5974
5975 freeSavedDisplayScreenshot(pu8Data);
5976
5977 return S_OK;
5978}
5979
5980STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5981{
5982 LogFlowThisFunc(("\n"));
5983
5984 CheckComArgNotNull(aWidth);
5985 CheckComArgNotNull(aHeight);
5986 CheckComArgOutSafeArrayPointerValid(aData);
5987
5988 if (aScreenId != 0)
5989 return E_NOTIMPL;
5990
5991 AutoCaller autoCaller(this);
5992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5993
5994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5995
5996 uint8_t *pu8Data = NULL;
5997 uint32_t cbData = 0;
5998 uint32_t u32Width = 0;
5999 uint32_t u32Height = 0;
6000
6001 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6002
6003 if (RT_FAILURE(vrc))
6004 return setError(VBOX_E_IPRT_ERROR,
6005 tr("Saved screenshot data is not available (%Rrc)"),
6006 vrc);
6007
6008 *aWidth = u32Width;
6009 *aHeight = u32Height;
6010
6011 com::SafeArray<BYTE> bitmap(cbData);
6012 /* Convert pixels to format expected by the API caller. */
6013 if (aBGR)
6014 {
6015 /* [0] B, [1] G, [2] R, [3] A. */
6016 for (unsigned i = 0; i < cbData; i += 4)
6017 {
6018 bitmap[i] = pu8Data[i];
6019 bitmap[i + 1] = pu8Data[i + 1];
6020 bitmap[i + 2] = pu8Data[i + 2];
6021 bitmap[i + 3] = 0xff;
6022 }
6023 }
6024 else
6025 {
6026 /* [0] R, [1] G, [2] B, [3] A. */
6027 for (unsigned i = 0; i < cbData; i += 4)
6028 {
6029 bitmap[i] = pu8Data[i + 2];
6030 bitmap[i + 1] = pu8Data[i + 1];
6031 bitmap[i + 2] = pu8Data[i];
6032 bitmap[i + 3] = 0xff;
6033 }
6034 }
6035 bitmap.detachTo(ComSafeArrayOutArg(aData));
6036
6037 freeSavedDisplayScreenshot(pu8Data);
6038
6039 return S_OK;
6040}
6041
6042
6043STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6044{
6045 LogFlowThisFunc(("\n"));
6046
6047 CheckComArgNotNull(aWidth);
6048 CheckComArgNotNull(aHeight);
6049 CheckComArgOutSafeArrayPointerValid(aData);
6050
6051 if (aScreenId != 0)
6052 return E_NOTIMPL;
6053
6054 AutoCaller autoCaller(this);
6055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6056
6057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6058
6059 uint8_t *pu8Data = NULL;
6060 uint32_t cbData = 0;
6061 uint32_t u32Width = 0;
6062 uint32_t u32Height = 0;
6063
6064 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6065
6066 if (RT_FAILURE(vrc))
6067 return setError(VBOX_E_IPRT_ERROR,
6068 tr("Saved screenshot data is not available (%Rrc)"),
6069 vrc);
6070
6071 *aWidth = u32Width;
6072 *aHeight = u32Height;
6073
6074 HRESULT rc = S_OK;
6075 uint8_t *pu8PNG = NULL;
6076 uint32_t cbPNG = 0;
6077 uint32_t cxPNG = 0;
6078 uint32_t cyPNG = 0;
6079
6080 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6081
6082 if (RT_SUCCESS(vrc))
6083 {
6084 com::SafeArray<BYTE> screenData(cbPNG);
6085 screenData.initFrom(pu8PNG, cbPNG);
6086 if (pu8PNG)
6087 RTMemFree(pu8PNG);
6088 screenData.detachTo(ComSafeArrayOutArg(aData));
6089 }
6090 else
6091 {
6092 if (pu8PNG)
6093 RTMemFree(pu8PNG);
6094 return setError(VBOX_E_IPRT_ERROR,
6095 tr("Could not convert screenshot to PNG (%Rrc)"),
6096 vrc);
6097 }
6098
6099 freeSavedDisplayScreenshot(pu8Data);
6100
6101 return rc;
6102}
6103
6104STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6105{
6106 LogFlowThisFunc(("\n"));
6107
6108 CheckComArgNotNull(aSize);
6109 CheckComArgNotNull(aWidth);
6110 CheckComArgNotNull(aHeight);
6111
6112 if (aScreenId != 0)
6113 return E_NOTIMPL;
6114
6115 AutoCaller autoCaller(this);
6116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6117
6118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6119
6120 uint8_t *pu8Data = NULL;
6121 uint32_t cbData = 0;
6122 uint32_t u32Width = 0;
6123 uint32_t u32Height = 0;
6124
6125 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6126
6127 if (RT_FAILURE(vrc))
6128 return setError(VBOX_E_IPRT_ERROR,
6129 tr("Saved screenshot data is not available (%Rrc)"),
6130 vrc);
6131
6132 *aSize = cbData;
6133 *aWidth = u32Width;
6134 *aHeight = u32Height;
6135
6136 freeSavedDisplayScreenshot(pu8Data);
6137
6138 return S_OK;
6139}
6140
6141STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6142{
6143 LogFlowThisFunc(("\n"));
6144
6145 CheckComArgNotNull(aWidth);
6146 CheckComArgNotNull(aHeight);
6147 CheckComArgOutSafeArrayPointerValid(aData);
6148
6149 if (aScreenId != 0)
6150 return E_NOTIMPL;
6151
6152 AutoCaller autoCaller(this);
6153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6154
6155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6156
6157 uint8_t *pu8Data = NULL;
6158 uint32_t cbData = 0;
6159 uint32_t u32Width = 0;
6160 uint32_t u32Height = 0;
6161
6162 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6163
6164 if (RT_FAILURE(vrc))
6165 return setError(VBOX_E_IPRT_ERROR,
6166 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6167 vrc);
6168
6169 *aWidth = u32Width;
6170 *aHeight = u32Height;
6171
6172 com::SafeArray<BYTE> png(cbData);
6173 png.initFrom(pu8Data, cbData);
6174 png.detachTo(ComSafeArrayOutArg(aData));
6175
6176 freeSavedDisplayScreenshot(pu8Data);
6177
6178 return S_OK;
6179}
6180
6181STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6182{
6183 HRESULT rc = S_OK;
6184 LogFlowThisFunc(("\n"));
6185
6186 AutoCaller autoCaller(this);
6187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6188
6189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6190
6191 if (!mHWData->mCPUHotPlugEnabled)
6192 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6193
6194 if (aCpu >= mHWData->mCPUCount)
6195 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6196
6197 if (mHWData->mCPUAttached[aCpu])
6198 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6199
6200 alock.release();
6201 rc = onCPUChange(aCpu, false);
6202 alock.acquire();
6203 if (FAILED(rc)) return rc;
6204
6205 setModified(IsModified_MachineData);
6206 mHWData.backup();
6207 mHWData->mCPUAttached[aCpu] = true;
6208
6209 /* Save settings if online */
6210 if (Global::IsOnline(mData->mMachineState))
6211 saveSettings(NULL);
6212
6213 return S_OK;
6214}
6215
6216STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6217{
6218 HRESULT rc = S_OK;
6219 LogFlowThisFunc(("\n"));
6220
6221 AutoCaller autoCaller(this);
6222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6223
6224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 if (!mHWData->mCPUHotPlugEnabled)
6227 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6228
6229 if (aCpu >= SchemaDefs::MaxCPUCount)
6230 return setError(E_INVALIDARG,
6231 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6232 SchemaDefs::MaxCPUCount);
6233
6234 if (!mHWData->mCPUAttached[aCpu])
6235 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6236
6237 /* CPU 0 can't be detached */
6238 if (aCpu == 0)
6239 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6240
6241 alock.release();
6242 rc = onCPUChange(aCpu, true);
6243 alock.acquire();
6244 if (FAILED(rc)) return rc;
6245
6246 setModified(IsModified_MachineData);
6247 mHWData.backup();
6248 mHWData->mCPUAttached[aCpu] = false;
6249
6250 /* Save settings if online */
6251 if (Global::IsOnline(mData->mMachineState))
6252 saveSettings(NULL);
6253
6254 return S_OK;
6255}
6256
6257STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6258{
6259 LogFlowThisFunc(("\n"));
6260
6261 CheckComArgNotNull(aCpuAttached);
6262
6263 *aCpuAttached = false;
6264
6265 AutoCaller autoCaller(this);
6266 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6267
6268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6269
6270 /* If hotplug is enabled the CPU is always enabled. */
6271 if (!mHWData->mCPUHotPlugEnabled)
6272 {
6273 if (aCpu < mHWData->mCPUCount)
6274 *aCpuAttached = true;
6275 }
6276 else
6277 {
6278 if (aCpu < SchemaDefs::MaxCPUCount)
6279 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6280 }
6281
6282 return S_OK;
6283}
6284
6285STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6286{
6287 CheckComArgOutPointerValid(aName);
6288
6289 AutoCaller autoCaller(this);
6290 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6291
6292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6293
6294 Utf8Str log = queryLogFilename(aIdx);
6295 if (!RTFileExists(log.c_str()))
6296 log.setNull();
6297 log.cloneTo(aName);
6298
6299 return S_OK;
6300}
6301
6302STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6303{
6304 LogFlowThisFunc(("\n"));
6305 CheckComArgOutSafeArrayPointerValid(aData);
6306 if (aSize < 0)
6307 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6308
6309 AutoCaller autoCaller(this);
6310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6311
6312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 HRESULT rc = S_OK;
6315 Utf8Str log = queryLogFilename(aIdx);
6316
6317 /* do not unnecessarily hold the lock while doing something which does
6318 * not need the lock and potentially takes a long time. */
6319 alock.release();
6320
6321 /* Limit the chunk size to 32K for now, as that gives better performance
6322 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6323 * One byte expands to approx. 25 bytes of breathtaking XML. */
6324 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6325 com::SafeArray<BYTE> logData(cbData);
6326
6327 RTFILE LogFile;
6328 int vrc = RTFileOpen(&LogFile, log.c_str(),
6329 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6330 if (RT_SUCCESS(vrc))
6331 {
6332 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6333 if (RT_SUCCESS(vrc))
6334 logData.resize(cbData);
6335 else
6336 rc = setError(VBOX_E_IPRT_ERROR,
6337 tr("Could not read log file '%s' (%Rrc)"),
6338 log.c_str(), vrc);
6339 RTFileClose(LogFile);
6340 }
6341 else
6342 rc = setError(VBOX_E_IPRT_ERROR,
6343 tr("Could not open log file '%s' (%Rrc)"),
6344 log.c_str(), vrc);
6345
6346 if (FAILED(rc))
6347 logData.resize(0);
6348 logData.detachTo(ComSafeArrayOutArg(aData));
6349
6350 return rc;
6351}
6352
6353
6354/**
6355 * Currently this method doesn't attach device to the running VM,
6356 * just makes sure it's plugged on next VM start.
6357 */
6358STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6359{
6360 AutoCaller autoCaller(this);
6361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6362
6363 // lock scope
6364 {
6365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6366
6367 HRESULT rc = checkStateDependency(MutableStateDep);
6368 if (FAILED(rc)) return rc;
6369
6370 ChipsetType_T aChipset = ChipsetType_PIIX3;
6371 COMGETTER(ChipsetType)(&aChipset);
6372
6373 if (aChipset != ChipsetType_ICH9)
6374 {
6375 return setError(E_INVALIDARG,
6376 tr("Host PCI attachment only supported with ICH9 chipset"));
6377 }
6378
6379 // check if device with this host PCI address already attached
6380 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6381 it != mHWData->mPciDeviceAssignments.end();
6382 ++it)
6383 {
6384 LONG iHostAddress = -1;
6385 ComPtr<PciDeviceAttachment> pAttach;
6386 pAttach = *it;
6387 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6388 if (iHostAddress == hostAddress)
6389 return setError(E_INVALIDARG,
6390 tr("Device with host PCI address already attached to this VM"));
6391 }
6392
6393 ComObjPtr<PciDeviceAttachment> pda;
6394 char name[32];
6395
6396 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6397 Bstr bname(name);
6398 pda.createObject();
6399 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6400 setModified(IsModified_MachineData);
6401 mHWData.backup();
6402 mHWData->mPciDeviceAssignments.push_back(pda);
6403 }
6404
6405 return S_OK;
6406}
6407
6408/**
6409 * Currently this method doesn't detach device from the running VM,
6410 * just makes sure it's not plugged on next VM start.
6411 */
6412STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress)
6413{
6414 AutoCaller autoCaller(this);
6415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6416
6417 ComObjPtr<PciDeviceAttachment> pAttach;
6418 bool fRemoved = false;
6419 HRESULT rc;
6420
6421 // lock scope
6422 {
6423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6424
6425 rc = checkStateDependency(MutableStateDep);
6426 if (FAILED(rc)) return rc;
6427
6428 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6429 it != mHWData->mPciDeviceAssignments.end();
6430 ++it)
6431 {
6432 LONG iHostAddress = -1;
6433 pAttach = *it;
6434 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6435 if (iHostAddress != -1 && iHostAddress == hostAddress)
6436 {
6437 setModified(IsModified_MachineData);
6438 mHWData.backup();
6439 mHWData->mPciDeviceAssignments.remove(pAttach);
6440 fRemoved = true;
6441 break;
6442 }
6443 }
6444 }
6445
6446
6447 /* Fire event outside of the lock */
6448 if (fRemoved)
6449 {
6450 Assert(!pAttach.isNull());
6451 ComPtr<IEventSource> es;
6452 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6453 Assert(SUCCEEDED(rc));
6454 Bstr mid;
6455 rc = this->COMGETTER(Id)(mid.asOutParam());
6456 Assert(SUCCEEDED(rc));
6457 fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6458 }
6459
6460 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6461 tr("No host PCI device %08x attached"),
6462 hostAddress
6463 );
6464}
6465
6466STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
6467{
6468 CheckComArgOutSafeArrayPointerValid(aAssignments);
6469
6470 AutoCaller autoCaller(this);
6471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6472
6473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6474
6475 SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments);
6476 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6477
6478 return S_OK;
6479}
6480
6481STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6482{
6483 CheckComArgOutPointerValid(aBandwidthControl);
6484
6485 AutoCaller autoCaller(this);
6486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6487
6488 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6489
6490 return S_OK;
6491}
6492
6493STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6494{
6495 CheckComArgOutPointerValid(pfEnabled);
6496 AutoCaller autoCaller(this);
6497 HRESULT hrc = autoCaller.rc();
6498 if (SUCCEEDED(hrc))
6499 {
6500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6501 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6502 }
6503 return hrc;
6504}
6505
6506STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6507{
6508 AutoCaller autoCaller(this);
6509 HRESULT hrc = autoCaller.rc();
6510 if (SUCCEEDED(hrc))
6511 {
6512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6513 hrc = checkStateDependency(MutableStateDep);
6514 if (SUCCEEDED(hrc))
6515 {
6516 hrc = mHWData.backupEx();
6517 if (SUCCEEDED(hrc))
6518 {
6519 setModified(IsModified_MachineData);
6520 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6521 }
6522 }
6523 }
6524 return hrc;
6525}
6526
6527STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6528{
6529 CheckComArgOutPointerValid(pbstrConfig);
6530 AutoCaller autoCaller(this);
6531 HRESULT hrc = autoCaller.rc();
6532 if (SUCCEEDED(hrc))
6533 {
6534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6535 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6536 }
6537 return hrc;
6538}
6539
6540STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6541{
6542 CheckComArgStr(bstrConfig);
6543 AutoCaller autoCaller(this);
6544 HRESULT hrc = autoCaller.rc();
6545 if (SUCCEEDED(hrc))
6546 {
6547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6548 hrc = checkStateDependency(MutableStateDep);
6549 if (SUCCEEDED(hrc))
6550 {
6551 hrc = mHWData.backupEx();
6552 if (SUCCEEDED(hrc))
6553 {
6554 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6555 if (SUCCEEDED(hrc))
6556 setModified(IsModified_MachineData);
6557 }
6558 }
6559 }
6560 return hrc;
6561
6562}
6563
6564STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6565{
6566 CheckComArgOutPointerValid(pfAllow);
6567 AutoCaller autoCaller(this);
6568 HRESULT hrc = autoCaller.rc();
6569 if (SUCCEEDED(hrc))
6570 {
6571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6572 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6573 }
6574 return hrc;
6575}
6576
6577STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6578{
6579 AutoCaller autoCaller(this);
6580 HRESULT hrc = autoCaller.rc();
6581 if (SUCCEEDED(hrc))
6582 {
6583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6584 hrc = checkStateDependency(MutableStateDep);
6585 if (SUCCEEDED(hrc))
6586 {
6587 hrc = mHWData.backupEx();
6588 if (SUCCEEDED(hrc))
6589 {
6590 setModified(IsModified_MachineData);
6591 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6592 }
6593 }
6594 }
6595 return hrc;
6596}
6597
6598STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6599{
6600 CheckComArgOutPointerValid(pfEnabled);
6601 AutoCaller autoCaller(this);
6602 HRESULT hrc = autoCaller.rc();
6603 if (SUCCEEDED(hrc))
6604 {
6605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6606 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6607 }
6608 return hrc;
6609}
6610
6611STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6612{
6613 AutoCaller autoCaller(this);
6614 HRESULT hrc = autoCaller.rc();
6615 if (SUCCEEDED(hrc))
6616 {
6617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6618 hrc = checkStateDependency(MutableStateDep);
6619 if ( SUCCEEDED(hrc)
6620 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6621 {
6622 AutostartDb *autostartDb = mParent->getAutostartDb();
6623 int vrc;
6624
6625 if (fEnabled)
6626 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6627 else
6628 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6629
6630 if (RT_SUCCESS(vrc))
6631 {
6632 hrc = mHWData.backupEx();
6633 if (SUCCEEDED(hrc))
6634 {
6635 setModified(IsModified_MachineData);
6636 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6637 }
6638 }
6639 else if (vrc == VERR_NOT_SUPPORTED)
6640 hrc = setError(VBOX_E_NOT_SUPPORTED,
6641 tr("The VM autostart feature is not supported on this platform"));
6642 else if (vrc == VERR_PATH_NOT_FOUND)
6643 hrc = setError(E_FAIL,
6644 tr("The path to the autostart database is not set"));
6645 else
6646 hrc = setError(E_UNEXPECTED,
6647 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6648 fEnabled ? "Adding" : "Removing",
6649 mUserData->s.strName.c_str(), vrc);
6650 }
6651 }
6652 return hrc;
6653}
6654
6655STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6656{
6657 CheckComArgOutPointerValid(puDelay);
6658 AutoCaller autoCaller(this);
6659 HRESULT hrc = autoCaller.rc();
6660 if (SUCCEEDED(hrc))
6661 {
6662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6663 *puDelay = mHWData->mAutostart.uAutostartDelay;
6664 }
6665 return hrc;
6666}
6667
6668STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6669{
6670 AutoCaller autoCaller(this);
6671 HRESULT hrc = autoCaller.rc();
6672 if (SUCCEEDED(hrc))
6673 {
6674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6675 hrc = checkStateDependency(MutableStateDep);
6676 if (SUCCEEDED(hrc))
6677 {
6678 hrc = mHWData.backupEx();
6679 if (SUCCEEDED(hrc))
6680 {
6681 setModified(IsModified_MachineData);
6682 mHWData->mAutostart.uAutostartDelay = uDelay;
6683 }
6684 }
6685 }
6686 return hrc;
6687}
6688
6689STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6690{
6691 CheckComArgOutPointerValid(penmAutostopType);
6692 AutoCaller autoCaller(this);
6693 HRESULT hrc = autoCaller.rc();
6694 if (SUCCEEDED(hrc))
6695 {
6696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6697 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6698 }
6699 return hrc;
6700}
6701
6702STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6703{
6704 AutoCaller autoCaller(this);
6705 HRESULT hrc = autoCaller.rc();
6706 if (SUCCEEDED(hrc))
6707 {
6708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6709 hrc = checkStateDependency(MutableStateDep);
6710 if ( SUCCEEDED(hrc)
6711 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6712 {
6713 AutostartDb *autostartDb = mParent->getAutostartDb();
6714 int vrc;
6715
6716 if (enmAutostopType != AutostopType_Disabled)
6717 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6718 else
6719 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6720
6721 if (RT_SUCCESS(vrc))
6722 {
6723 hrc = mHWData.backupEx();
6724 if (SUCCEEDED(hrc))
6725 {
6726 setModified(IsModified_MachineData);
6727 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6728 }
6729 }
6730 else if (vrc == VERR_NOT_SUPPORTED)
6731 hrc = setError(VBOX_E_NOT_SUPPORTED,
6732 tr("The VM autostop feature is not supported on this platform"));
6733 else if (vrc == VERR_PATH_NOT_FOUND)
6734 hrc = setError(E_FAIL,
6735 tr("The path to the autostart database is not set"));
6736 else
6737 hrc = setError(E_UNEXPECTED,
6738 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6739 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6740 mUserData->s.strName.c_str(), vrc);
6741 }
6742 }
6743 return hrc;
6744}
6745
6746
6747STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6748{
6749 LogFlowFuncEnter();
6750
6751 CheckComArgNotNull(pTarget);
6752 CheckComArgOutPointerValid(pProgress);
6753
6754 /* Convert the options. */
6755 RTCList<CloneOptions_T> optList;
6756 if (options != NULL)
6757 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6758
6759 if (optList.contains(CloneOptions_Link))
6760 {
6761 if (!isSnapshotMachine())
6762 return setError(E_INVALIDARG,
6763 tr("Linked clone can only be created from a snapshot"));
6764 if (mode != CloneMode_MachineState)
6765 return setError(E_INVALIDARG,
6766 tr("Linked clone can only be created for a single machine state"));
6767 }
6768 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6769
6770 AutoCaller autoCaller(this);
6771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6772
6773
6774 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6775
6776 HRESULT rc = pWorker->start(pProgress);
6777
6778 LogFlowFuncLeave();
6779
6780 return rc;
6781}
6782
6783// public methods for internal purposes
6784/////////////////////////////////////////////////////////////////////////////
6785
6786/**
6787 * Adds the given IsModified_* flag to the dirty flags of the machine.
6788 * This must be called either during loadSettings or under the machine write lock.
6789 * @param fl
6790 */
6791void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6792{
6793 mData->flModifications |= fl;
6794 if (fAllowStateModification && isStateModificationAllowed())
6795 mData->mCurrentStateModified = true;
6796}
6797
6798/**
6799 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6800 * care of the write locking.
6801 *
6802 * @param fModifications The flag to add.
6803 */
6804void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6805{
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807 setModified(fModification, fAllowStateModification);
6808}
6809
6810/**
6811 * Saves the registry entry of this machine to the given configuration node.
6812 *
6813 * @param aEntryNode Node to save the registry entry to.
6814 *
6815 * @note locks this object for reading.
6816 */
6817HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6818{
6819 AutoLimitedCaller autoCaller(this);
6820 AssertComRCReturnRC(autoCaller.rc());
6821
6822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6823
6824 data.uuid = mData->mUuid;
6825 data.strSettingsFile = mData->m_strConfigFile;
6826
6827 return S_OK;
6828}
6829
6830/**
6831 * Calculates the absolute path of the given path taking the directory of the
6832 * machine settings file as the current directory.
6833 *
6834 * @param aPath Path to calculate the absolute path for.
6835 * @param aResult Where to put the result (used only on success, can be the
6836 * same Utf8Str instance as passed in @a aPath).
6837 * @return IPRT result.
6838 *
6839 * @note Locks this object for reading.
6840 */
6841int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6842{
6843 AutoCaller autoCaller(this);
6844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6845
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6849
6850 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6851
6852 strSettingsDir.stripFilename();
6853 char folder[RTPATH_MAX];
6854 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6855 if (RT_SUCCESS(vrc))
6856 aResult = folder;
6857
6858 return vrc;
6859}
6860
6861/**
6862 * Copies strSource to strTarget, making it relative to the machine folder
6863 * if it is a subdirectory thereof, or simply copying it otherwise.
6864 *
6865 * @param strSource Path to evaluate and copy.
6866 * @param strTarget Buffer to receive target path.
6867 *
6868 * @note Locks this object for reading.
6869 */
6870void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6871 Utf8Str &strTarget)
6872{
6873 AutoCaller autoCaller(this);
6874 AssertComRCReturn(autoCaller.rc(), (void)0);
6875
6876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6877
6878 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6879 // use strTarget as a temporary buffer to hold the machine settings dir
6880 strTarget = mData->m_strConfigFileFull;
6881 strTarget.stripFilename();
6882 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6883 {
6884 // is relative: then append what's left
6885 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6886 // for empty paths (only possible for subdirs) use "." to avoid
6887 // triggering default settings for not present config attributes.
6888 if (strTarget.isEmpty())
6889 strTarget = ".";
6890 }
6891 else
6892 // is not relative: then overwrite
6893 strTarget = strSource;
6894}
6895
6896/**
6897 * Returns the full path to the machine's log folder in the
6898 * \a aLogFolder argument.
6899 */
6900void Machine::getLogFolder(Utf8Str &aLogFolder)
6901{
6902 AutoCaller autoCaller(this);
6903 AssertComRCReturnVoid(autoCaller.rc());
6904
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906
6907 char szTmp[RTPATH_MAX];
6908 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6909 if (RT_SUCCESS(vrc))
6910 {
6911 if (szTmp[0] && !mUserData.isNull())
6912 {
6913 char szTmp2[RTPATH_MAX];
6914 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6915 if (RT_SUCCESS(vrc))
6916 aLogFolder = BstrFmt("%s%c%s",
6917 szTmp2,
6918 RTPATH_DELIMITER,
6919 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6920 }
6921 else
6922 vrc = VERR_PATH_IS_RELATIVE;
6923 }
6924
6925 if (RT_FAILURE(vrc))
6926 {
6927 // fallback if VBOX_USER_LOGHOME is not set or invalid
6928 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6929 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6930 aLogFolder.append(RTPATH_DELIMITER);
6931 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6932 }
6933}
6934
6935/**
6936 * Returns the full path to the machine's log file for an given index.
6937 */
6938Utf8Str Machine::queryLogFilename(ULONG idx)
6939{
6940 Utf8Str logFolder;
6941 getLogFolder(logFolder);
6942 Assert(logFolder.length());
6943 Utf8Str log;
6944 if (idx == 0)
6945 log = Utf8StrFmt("%s%cVBox.log",
6946 logFolder.c_str(), RTPATH_DELIMITER);
6947 else
6948 log = Utf8StrFmt("%s%cVBox.log.%d",
6949 logFolder.c_str(), RTPATH_DELIMITER, idx);
6950 return log;
6951}
6952
6953/**
6954 * Composes a unique saved state filename based on the current system time. The filename is
6955 * granular to the second so this will work so long as no more than one snapshot is taken on
6956 * a machine per second.
6957 *
6958 * Before version 4.1, we used this formula for saved state files:
6959 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
6960 * which no longer works because saved state files can now be shared between the saved state of the
6961 * "saved" machine and an online snapshot, and the following would cause problems:
6962 * 1) save machine
6963 * 2) create online snapshot from that machine state --> reusing saved state file
6964 * 3) save machine again --> filename would be reused, breaking the online snapshot
6965 *
6966 * So instead we now use a timestamp.
6967 *
6968 * @param str
6969 */
6970void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
6971{
6972 AutoCaller autoCaller(this);
6973 AssertComRCReturnVoid(autoCaller.rc());
6974
6975 {
6976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6977 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
6978 }
6979
6980 RTTIMESPEC ts;
6981 RTTimeNow(&ts);
6982 RTTIME time;
6983 RTTimeExplode(&time, &ts);
6984
6985 strStateFilePath += RTPATH_DELIMITER;
6986 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
6987 time.i32Year, time.u8Month, time.u8MonthDay,
6988 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
6989}
6990
6991/**
6992 * @note Locks this object for writing, calls the client process
6993 * (inside the lock).
6994 */
6995HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
6996 const Utf8Str &strType,
6997 const Utf8Str &strEnvironment,
6998 ProgressProxy *aProgress)
6999{
7000 LogFlowThisFuncEnter();
7001
7002 AssertReturn(aControl, E_FAIL);
7003 AssertReturn(aProgress, E_FAIL);
7004
7005 AutoCaller autoCaller(this);
7006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7007
7008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7009
7010 if (!mData->mRegistered)
7011 return setError(E_UNEXPECTED,
7012 tr("The machine '%s' is not registered"),
7013 mUserData->s.strName.c_str());
7014
7015 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7016
7017 if ( mData->mSession.mState == SessionState_Locked
7018 || mData->mSession.mState == SessionState_Spawning
7019 || mData->mSession.mState == SessionState_Unlocking)
7020 return setError(VBOX_E_INVALID_OBJECT_STATE,
7021 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7022 mUserData->s.strName.c_str());
7023
7024 /* may not be busy */
7025 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7026
7027 /* get the path to the executable */
7028 char szPath[RTPATH_MAX];
7029 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7030 size_t sz = strlen(szPath);
7031 szPath[sz++] = RTPATH_DELIMITER;
7032 szPath[sz] = 0;
7033 char *cmd = szPath + sz;
7034 sz = RTPATH_MAX - sz;
7035
7036 int vrc = VINF_SUCCESS;
7037 RTPROCESS pid = NIL_RTPROCESS;
7038
7039 RTENV env = RTENV_DEFAULT;
7040
7041 if (!strEnvironment.isEmpty())
7042 {
7043 char *newEnvStr = NULL;
7044
7045 do
7046 {
7047 /* clone the current environment */
7048 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7049 AssertRCBreakStmt(vrc2, vrc = vrc2);
7050
7051 newEnvStr = RTStrDup(strEnvironment.c_str());
7052 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7053
7054 /* put new variables to the environment
7055 * (ignore empty variable names here since RTEnv API
7056 * intentionally doesn't do that) */
7057 char *var = newEnvStr;
7058 for (char *p = newEnvStr; *p; ++p)
7059 {
7060 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7061 {
7062 *p = '\0';
7063 if (*var)
7064 {
7065 char *val = strchr(var, '=');
7066 if (val)
7067 {
7068 *val++ = '\0';
7069 vrc2 = RTEnvSetEx(env, var, val);
7070 }
7071 else
7072 vrc2 = RTEnvUnsetEx(env, var);
7073 if (RT_FAILURE(vrc2))
7074 break;
7075 }
7076 var = p + 1;
7077 }
7078 }
7079 if (RT_SUCCESS(vrc2) && *var)
7080 vrc2 = RTEnvPutEx(env, var);
7081
7082 AssertRCBreakStmt(vrc2, vrc = vrc2);
7083 }
7084 while (0);
7085
7086 if (newEnvStr != NULL)
7087 RTStrFree(newEnvStr);
7088 }
7089
7090 /* Qt is default */
7091#ifdef VBOX_WITH_QTGUI
7092 if (strType == "gui" || strType == "GUI/Qt")
7093 {
7094# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7095 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7096# else
7097 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7098# endif
7099 Assert(sz >= sizeof(VirtualBox_exe));
7100 strcpy(cmd, VirtualBox_exe);
7101
7102 Utf8Str idStr = mData->mUuid.toString();
7103 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7104 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7105 }
7106#else /* !VBOX_WITH_QTGUI */
7107 if (0)
7108 ;
7109#endif /* VBOX_WITH_QTGUI */
7110
7111 else
7112
7113#ifdef VBOX_WITH_VBOXSDL
7114 if (strType == "sdl" || strType == "GUI/SDL")
7115 {
7116 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7117 Assert(sz >= sizeof(VBoxSDL_exe));
7118 strcpy(cmd, VBoxSDL_exe);
7119
7120 Utf8Str idStr = mData->mUuid.toString();
7121 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7122 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7123 }
7124#else /* !VBOX_WITH_VBOXSDL */
7125 if (0)
7126 ;
7127#endif /* !VBOX_WITH_VBOXSDL */
7128
7129 else
7130
7131#ifdef VBOX_WITH_HEADLESS
7132 if ( strType == "headless"
7133 || strType == "capture"
7134 || strType == "vrdp" /* Deprecated. Same as headless. */
7135 )
7136 {
7137 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7138 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7139 * and a VM works even if the server has not been installed.
7140 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7141 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7142 * differently in 4.0 and 3.x.
7143 */
7144 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7145 Assert(sz >= sizeof(VBoxHeadless_exe));
7146 strcpy(cmd, VBoxHeadless_exe);
7147
7148 Utf8Str idStr = mData->mUuid.toString();
7149 /* Leave space for "--capture" arg. */
7150 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7151 "--startvm", idStr.c_str(),
7152 "--vrde", "config",
7153 0, /* For "--capture". */
7154 0 };
7155 if (strType == "capture")
7156 {
7157 unsigned pos = RT_ELEMENTS(args) - 2;
7158 args[pos] = "--capture";
7159 }
7160 vrc = RTProcCreate(szPath, args, env,
7161#ifdef RT_OS_WINDOWS
7162 RTPROC_FLAGS_NO_WINDOW
7163#else
7164 0
7165#endif
7166 , &pid);
7167 }
7168#else /* !VBOX_WITH_HEADLESS */
7169 if (0)
7170 ;
7171#endif /* !VBOX_WITH_HEADLESS */
7172 else
7173 {
7174 RTEnvDestroy(env);
7175 return setError(E_INVALIDARG,
7176 tr("Invalid session type: '%s'"),
7177 strType.c_str());
7178 }
7179
7180 RTEnvDestroy(env);
7181
7182 if (RT_FAILURE(vrc))
7183 return setError(VBOX_E_IPRT_ERROR,
7184 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7185 mUserData->s.strName.c_str(), vrc);
7186
7187 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7188
7189 /*
7190 * Note that we don't release the lock here before calling the client,
7191 * because it doesn't need to call us back if called with a NULL argument.
7192 * Releasing the lock here is dangerous because we didn't prepare the
7193 * launch data yet, but the client we've just started may happen to be
7194 * too fast and call openSession() that will fail (because of PID, etc.),
7195 * so that the Machine will never get out of the Spawning session state.
7196 */
7197
7198 /* inform the session that it will be a remote one */
7199 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7200 HRESULT rc = aControl->AssignMachine(NULL);
7201 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7202
7203 if (FAILED(rc))
7204 {
7205 /* restore the session state */
7206 mData->mSession.mState = SessionState_Unlocked;
7207 /* The failure may occur w/o any error info (from RPC), so provide one */
7208 return setError(VBOX_E_VM_ERROR,
7209 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7210 }
7211
7212 /* attach launch data to the machine */
7213 Assert(mData->mSession.mPid == NIL_RTPROCESS);
7214 mData->mSession.mRemoteControls.push_back(aControl);
7215 mData->mSession.mProgress = aProgress;
7216 mData->mSession.mPid = pid;
7217 mData->mSession.mState = SessionState_Spawning;
7218 mData->mSession.mType = strType;
7219
7220 LogFlowThisFuncLeave();
7221 return S_OK;
7222}
7223
7224/**
7225 * Returns @c true if the given machine has an open direct session and returns
7226 * the session machine instance and additional session data (on some platforms)
7227 * if so.
7228 *
7229 * Note that when the method returns @c false, the arguments remain unchanged.
7230 *
7231 * @param aMachine Session machine object.
7232 * @param aControl Direct session control object (optional).
7233 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7234 *
7235 * @note locks this object for reading.
7236 */
7237#if defined(RT_OS_WINDOWS)
7238bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7239 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7240 HANDLE *aIPCSem /*= NULL*/,
7241 bool aAllowClosing /*= false*/)
7242#elif defined(RT_OS_OS2)
7243bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7244 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7245 HMTX *aIPCSem /*= NULL*/,
7246 bool aAllowClosing /*= false*/)
7247#else
7248bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7249 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7250 bool aAllowClosing /*= false*/)
7251#endif
7252{
7253 AutoLimitedCaller autoCaller(this);
7254 AssertComRCReturn(autoCaller.rc(), false);
7255
7256 /* just return false for inaccessible machines */
7257 if (autoCaller.state() != Ready)
7258 return false;
7259
7260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7261
7262 if ( mData->mSession.mState == SessionState_Locked
7263 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7264 )
7265 {
7266 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7267
7268 aMachine = mData->mSession.mMachine;
7269
7270 if (aControl != NULL)
7271 *aControl = mData->mSession.mDirectControl;
7272
7273#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7274 /* Additional session data */
7275 if (aIPCSem != NULL)
7276 *aIPCSem = aMachine->mIPCSem;
7277#endif
7278 return true;
7279 }
7280
7281 return false;
7282}
7283
7284/**
7285 * Returns @c true if the given machine has an spawning direct session and
7286 * returns and additional session data (on some platforms) if so.
7287 *
7288 * Note that when the method returns @c false, the arguments remain unchanged.
7289 *
7290 * @param aPID PID of the spawned direct session process.
7291 *
7292 * @note locks this object for reading.
7293 */
7294#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7295bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7296#else
7297bool Machine::isSessionSpawning()
7298#endif
7299{
7300 AutoLimitedCaller autoCaller(this);
7301 AssertComRCReturn(autoCaller.rc(), false);
7302
7303 /* just return false for inaccessible machines */
7304 if (autoCaller.state() != Ready)
7305 return false;
7306
7307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7308
7309 if (mData->mSession.mState == SessionState_Spawning)
7310 {
7311#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7312 /* Additional session data */
7313 if (aPID != NULL)
7314 {
7315 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
7316 *aPID = mData->mSession.mPid;
7317 }
7318#endif
7319 return true;
7320 }
7321
7322 return false;
7323}
7324
7325/**
7326 * Called from the client watcher thread to check for unexpected client process
7327 * death during Session_Spawning state (e.g. before it successfully opened a
7328 * direct session).
7329 *
7330 * On Win32 and on OS/2, this method is called only when we've got the
7331 * direct client's process termination notification, so it always returns @c
7332 * true.
7333 *
7334 * On other platforms, this method returns @c true if the client process is
7335 * terminated and @c false if it's still alive.
7336 *
7337 * @note Locks this object for writing.
7338 */
7339bool Machine::checkForSpawnFailure()
7340{
7341 AutoCaller autoCaller(this);
7342 if (!autoCaller.isOk())
7343 {
7344 /* nothing to do */
7345 LogFlowThisFunc(("Already uninitialized!\n"));
7346 return true;
7347 }
7348
7349 /* VirtualBox::addProcessToReap() needs a write lock */
7350 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7351
7352 if (mData->mSession.mState != SessionState_Spawning)
7353 {
7354 /* nothing to do */
7355 LogFlowThisFunc(("Not spawning any more!\n"));
7356 return true;
7357 }
7358
7359 HRESULT rc = S_OK;
7360
7361#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7362
7363 /* the process was already unexpectedly terminated, we just need to set an
7364 * error and finalize session spawning */
7365 rc = setError(E_FAIL,
7366 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7367 getName().c_str());
7368#else
7369
7370 /* PID not yet initialized, skip check. */
7371 if (mData->mSession.mPid == NIL_RTPROCESS)
7372 return false;
7373
7374 RTPROCSTATUS status;
7375 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
7376 &status);
7377
7378 if (vrc != VERR_PROCESS_RUNNING)
7379 {
7380 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7381 rc = setError(E_FAIL,
7382 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7383 getName().c_str(), status.iStatus);
7384 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7385 rc = setError(E_FAIL,
7386 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7387 getName().c_str(), status.iStatus);
7388 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7389 rc = setError(E_FAIL,
7390 tr("The virtual machine '%s' has terminated abnormally"),
7391 getName().c_str(), status.iStatus);
7392 else
7393 rc = setError(E_FAIL,
7394 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7395 getName().c_str(), rc);
7396 }
7397
7398#endif
7399
7400 if (FAILED(rc))
7401 {
7402 /* Close the remote session, remove the remote control from the list
7403 * and reset session state to Closed (@note keep the code in sync with
7404 * the relevant part in checkForSpawnFailure()). */
7405
7406 Assert(mData->mSession.mRemoteControls.size() == 1);
7407 if (mData->mSession.mRemoteControls.size() == 1)
7408 {
7409 ErrorInfoKeeper eik;
7410 mData->mSession.mRemoteControls.front()->Uninitialize();
7411 }
7412
7413 mData->mSession.mRemoteControls.clear();
7414 mData->mSession.mState = SessionState_Unlocked;
7415
7416 /* finalize the progress after setting the state */
7417 if (!mData->mSession.mProgress.isNull())
7418 {
7419 mData->mSession.mProgress->notifyComplete(rc);
7420 mData->mSession.mProgress.setNull();
7421 }
7422
7423 mParent->addProcessToReap(mData->mSession.mPid);
7424 mData->mSession.mPid = NIL_RTPROCESS;
7425
7426 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7427 return true;
7428 }
7429
7430 return false;
7431}
7432
7433/**
7434 * Checks whether the machine can be registered. If so, commits and saves
7435 * all settings.
7436 *
7437 * @note Must be called from mParent's write lock. Locks this object and
7438 * children for writing.
7439 */
7440HRESULT Machine::prepareRegister()
7441{
7442 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7443
7444 AutoLimitedCaller autoCaller(this);
7445 AssertComRCReturnRC(autoCaller.rc());
7446
7447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7448
7449 /* wait for state dependents to drop to zero */
7450 ensureNoStateDependencies();
7451
7452 if (!mData->mAccessible)
7453 return setError(VBOX_E_INVALID_OBJECT_STATE,
7454 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7455 mUserData->s.strName.c_str(),
7456 mData->mUuid.toString().c_str());
7457
7458 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7459
7460 if (mData->mRegistered)
7461 return setError(VBOX_E_INVALID_OBJECT_STATE,
7462 tr("The machine '%s' with UUID {%s} is already registered"),
7463 mUserData->s.strName.c_str(),
7464 mData->mUuid.toString().c_str());
7465
7466 HRESULT rc = S_OK;
7467
7468 // Ensure the settings are saved. If we are going to be registered and
7469 // no config file exists yet, create it by calling saveSettings() too.
7470 if ( (mData->flModifications)
7471 || (!mData->pMachineConfigFile->fileExists())
7472 )
7473 {
7474 rc = saveSettings(NULL);
7475 // no need to check whether VirtualBox.xml needs saving too since
7476 // we can't have a machine XML file rename pending
7477 if (FAILED(rc)) return rc;
7478 }
7479
7480 /* more config checking goes here */
7481
7482 if (SUCCEEDED(rc))
7483 {
7484 /* we may have had implicit modifications we want to fix on success */
7485 commit();
7486
7487 mData->mRegistered = true;
7488 }
7489 else
7490 {
7491 /* we may have had implicit modifications we want to cancel on failure*/
7492 rollback(false /* aNotify */);
7493 }
7494
7495 return rc;
7496}
7497
7498/**
7499 * Increases the number of objects dependent on the machine state or on the
7500 * registered state. Guarantees that these two states will not change at least
7501 * until #releaseStateDependency() is called.
7502 *
7503 * Depending on the @a aDepType value, additional state checks may be made.
7504 * These checks will set extended error info on failure. See
7505 * #checkStateDependency() for more info.
7506 *
7507 * If this method returns a failure, the dependency is not added and the caller
7508 * is not allowed to rely on any particular machine state or registration state
7509 * value and may return the failed result code to the upper level.
7510 *
7511 * @param aDepType Dependency type to add.
7512 * @param aState Current machine state (NULL if not interested).
7513 * @param aRegistered Current registered state (NULL if not interested).
7514 *
7515 * @note Locks this object for writing.
7516 */
7517HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7518 MachineState_T *aState /* = NULL */,
7519 BOOL *aRegistered /* = NULL */)
7520{
7521 AutoCaller autoCaller(this);
7522 AssertComRCReturnRC(autoCaller.rc());
7523
7524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7525
7526 HRESULT rc = checkStateDependency(aDepType);
7527 if (FAILED(rc)) return rc;
7528
7529 {
7530 if (mData->mMachineStateChangePending != 0)
7531 {
7532 /* ensureNoStateDependencies() is waiting for state dependencies to
7533 * drop to zero so don't add more. It may make sense to wait a bit
7534 * and retry before reporting an error (since the pending state
7535 * transition should be really quick) but let's just assert for
7536 * now to see if it ever happens on practice. */
7537
7538 AssertFailed();
7539
7540 return setError(E_ACCESSDENIED,
7541 tr("Machine state change is in progress. Please retry the operation later."));
7542 }
7543
7544 ++mData->mMachineStateDeps;
7545 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7546 }
7547
7548 if (aState)
7549 *aState = mData->mMachineState;
7550 if (aRegistered)
7551 *aRegistered = mData->mRegistered;
7552
7553 return S_OK;
7554}
7555
7556/**
7557 * Decreases the number of objects dependent on the machine state.
7558 * Must always complete the #addStateDependency() call after the state
7559 * dependency is no more necessary.
7560 */
7561void Machine::releaseStateDependency()
7562{
7563 AutoCaller autoCaller(this);
7564 AssertComRCReturnVoid(autoCaller.rc());
7565
7566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7567
7568 /* releaseStateDependency() w/o addStateDependency()? */
7569 AssertReturnVoid(mData->mMachineStateDeps != 0);
7570 -- mData->mMachineStateDeps;
7571
7572 if (mData->mMachineStateDeps == 0)
7573 {
7574 /* inform ensureNoStateDependencies() that there are no more deps */
7575 if (mData->mMachineStateChangePending != 0)
7576 {
7577 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7578 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7579 }
7580 }
7581}
7582
7583// protected methods
7584/////////////////////////////////////////////////////////////////////////////
7585
7586/**
7587 * Performs machine state checks based on the @a aDepType value. If a check
7588 * fails, this method will set extended error info, otherwise it will return
7589 * S_OK. It is supposed, that on failure, the caller will immediately return
7590 * the return value of this method to the upper level.
7591 *
7592 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7593 *
7594 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7595 * current state of this machine object allows to change settings of the
7596 * machine (i.e. the machine is not registered, or registered but not running
7597 * and not saved). It is useful to call this method from Machine setters
7598 * before performing any change.
7599 *
7600 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7601 * as for MutableStateDep except that if the machine is saved, S_OK is also
7602 * returned. This is useful in setters which allow changing machine
7603 * properties when it is in the saved state.
7604 *
7605 * @param aDepType Dependency type to check.
7606 *
7607 * @note Non Machine based classes should use #addStateDependency() and
7608 * #releaseStateDependency() methods or the smart AutoStateDependency
7609 * template.
7610 *
7611 * @note This method must be called from under this object's read or write
7612 * lock.
7613 */
7614HRESULT Machine::checkStateDependency(StateDependency aDepType)
7615{
7616 switch (aDepType)
7617 {
7618 case AnyStateDep:
7619 {
7620 break;
7621 }
7622 case MutableStateDep:
7623 {
7624 if ( mData->mRegistered
7625 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7626 || ( mData->mMachineState != MachineState_Paused
7627 && mData->mMachineState != MachineState_Running
7628 && mData->mMachineState != MachineState_Aborted
7629 && mData->mMachineState != MachineState_Teleported
7630 && mData->mMachineState != MachineState_PoweredOff
7631 )
7632 )
7633 )
7634 return setError(VBOX_E_INVALID_VM_STATE,
7635 tr("The machine is not mutable (state is %s)"),
7636 Global::stringifyMachineState(mData->mMachineState));
7637 break;
7638 }
7639 case MutableOrSavedStateDep:
7640 {
7641 if ( mData->mRegistered
7642 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7643 || ( mData->mMachineState != MachineState_Paused
7644 && mData->mMachineState != MachineState_Running
7645 && mData->mMachineState != MachineState_Aborted
7646 && mData->mMachineState != MachineState_Teleported
7647 && mData->mMachineState != MachineState_Saved
7648 && mData->mMachineState != MachineState_PoweredOff
7649 )
7650 )
7651 )
7652 return setError(VBOX_E_INVALID_VM_STATE,
7653 tr("The machine is not mutable (state is %s)"),
7654 Global::stringifyMachineState(mData->mMachineState));
7655 break;
7656 }
7657 }
7658
7659 return S_OK;
7660}
7661
7662/**
7663 * Helper to initialize all associated child objects and allocate data
7664 * structures.
7665 *
7666 * This method must be called as a part of the object's initialization procedure
7667 * (usually done in the #init() method).
7668 *
7669 * @note Must be called only from #init() or from #registeredInit().
7670 */
7671HRESULT Machine::initDataAndChildObjects()
7672{
7673 AutoCaller autoCaller(this);
7674 AssertComRCReturnRC(autoCaller.rc());
7675 AssertComRCReturn(autoCaller.state() == InInit ||
7676 autoCaller.state() == Limited, E_FAIL);
7677
7678 AssertReturn(!mData->mAccessible, E_FAIL);
7679
7680 /* allocate data structures */
7681 mSSData.allocate();
7682 mUserData.allocate();
7683 mHWData.allocate();
7684 mMediaData.allocate();
7685 mStorageControllers.allocate();
7686
7687 /* initialize mOSTypeId */
7688 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7689
7690 /* create associated BIOS settings object */
7691 unconst(mBIOSSettings).createObject();
7692 mBIOSSettings->init(this);
7693
7694 /* create an associated VRDE object (default is disabled) */
7695 unconst(mVRDEServer).createObject();
7696 mVRDEServer->init(this);
7697
7698 /* create associated serial port objects */
7699 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7700 {
7701 unconst(mSerialPorts[slot]).createObject();
7702 mSerialPorts[slot]->init(this, slot);
7703 }
7704
7705 /* create associated parallel port objects */
7706 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7707 {
7708 unconst(mParallelPorts[slot]).createObject();
7709 mParallelPorts[slot]->init(this, slot);
7710 }
7711
7712 /* create the audio adapter object (always present, default is disabled) */
7713 unconst(mAudioAdapter).createObject();
7714 mAudioAdapter->init(this);
7715
7716 /* create the USB controller object (always present, default is disabled) */
7717 unconst(mUSBController).createObject();
7718 mUSBController->init(this);
7719
7720 /* create associated network adapter objects */
7721 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7722 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7723 {
7724 unconst(mNetworkAdapters[slot]).createObject();
7725 mNetworkAdapters[slot]->init(this, slot);
7726 }
7727
7728 /* create the bandwidth control */
7729 unconst(mBandwidthControl).createObject();
7730 mBandwidthControl->init(this);
7731
7732 return S_OK;
7733}
7734
7735/**
7736 * Helper to uninitialize all associated child objects and to free all data
7737 * structures.
7738 *
7739 * This method must be called as a part of the object's uninitialization
7740 * procedure (usually done in the #uninit() method).
7741 *
7742 * @note Must be called only from #uninit() or from #registeredInit().
7743 */
7744void Machine::uninitDataAndChildObjects()
7745{
7746 AutoCaller autoCaller(this);
7747 AssertComRCReturnVoid(autoCaller.rc());
7748 AssertComRCReturnVoid( autoCaller.state() == InUninit
7749 || autoCaller.state() == Limited);
7750
7751 /* tell all our other child objects we've been uninitialized */
7752 if (mBandwidthControl)
7753 {
7754 mBandwidthControl->uninit();
7755 unconst(mBandwidthControl).setNull();
7756 }
7757
7758 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7759 {
7760 if (mNetworkAdapters[slot])
7761 {
7762 mNetworkAdapters[slot]->uninit();
7763 unconst(mNetworkAdapters[slot]).setNull();
7764 }
7765 }
7766
7767 if (mUSBController)
7768 {
7769 mUSBController->uninit();
7770 unconst(mUSBController).setNull();
7771 }
7772
7773 if (mAudioAdapter)
7774 {
7775 mAudioAdapter->uninit();
7776 unconst(mAudioAdapter).setNull();
7777 }
7778
7779 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7780 {
7781 if (mParallelPorts[slot])
7782 {
7783 mParallelPorts[slot]->uninit();
7784 unconst(mParallelPorts[slot]).setNull();
7785 }
7786 }
7787
7788 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7789 {
7790 if (mSerialPorts[slot])
7791 {
7792 mSerialPorts[slot]->uninit();
7793 unconst(mSerialPorts[slot]).setNull();
7794 }
7795 }
7796
7797 if (mVRDEServer)
7798 {
7799 mVRDEServer->uninit();
7800 unconst(mVRDEServer).setNull();
7801 }
7802
7803 if (mBIOSSettings)
7804 {
7805 mBIOSSettings->uninit();
7806 unconst(mBIOSSettings).setNull();
7807 }
7808
7809 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7810 * instance is uninitialized; SessionMachine instances refer to real
7811 * Machine hard disks). This is necessary for a clean re-initialization of
7812 * the VM after successfully re-checking the accessibility state. Note
7813 * that in case of normal Machine or SnapshotMachine uninitialization (as
7814 * a result of unregistering or deleting the snapshot), outdated hard
7815 * disk attachments will already be uninitialized and deleted, so this
7816 * code will not affect them. */
7817 if ( !!mMediaData
7818 && (!isSessionMachine())
7819 )
7820 {
7821 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7822 it != mMediaData->mAttachments.end();
7823 ++it)
7824 {
7825 ComObjPtr<Medium> hd = (*it)->getMedium();
7826 if (hd.isNull())
7827 continue;
7828 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7829 AssertComRC(rc);
7830 }
7831 }
7832
7833 if (!isSessionMachine() && !isSnapshotMachine())
7834 {
7835 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7836 if (mData->mFirstSnapshot)
7837 {
7838 // snapshots tree is protected by media write lock; strictly
7839 // this isn't necessary here since we're deleting the entire
7840 // machine, but otherwise we assert in Snapshot::uninit()
7841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7842 mData->mFirstSnapshot->uninit();
7843 mData->mFirstSnapshot.setNull();
7844 }
7845
7846 mData->mCurrentSnapshot.setNull();
7847 }
7848
7849 /* free data structures (the essential mData structure is not freed here
7850 * since it may be still in use) */
7851 mMediaData.free();
7852 mStorageControllers.free();
7853 mHWData.free();
7854 mUserData.free();
7855 mSSData.free();
7856}
7857
7858/**
7859 * Returns a pointer to the Machine object for this machine that acts like a
7860 * parent for complex machine data objects such as shared folders, etc.
7861 *
7862 * For primary Machine objects and for SnapshotMachine objects, returns this
7863 * object's pointer itself. For SessionMachine objects, returns the peer
7864 * (primary) machine pointer.
7865 */
7866Machine* Machine::getMachine()
7867{
7868 if (isSessionMachine())
7869 return (Machine*)mPeer;
7870 return this;
7871}
7872
7873/**
7874 * Makes sure that there are no machine state dependents. If necessary, waits
7875 * for the number of dependents to drop to zero.
7876 *
7877 * Make sure this method is called from under this object's write lock to
7878 * guarantee that no new dependents may be added when this method returns
7879 * control to the caller.
7880 *
7881 * @note Locks this object for writing. The lock will be released while waiting
7882 * (if necessary).
7883 *
7884 * @warning To be used only in methods that change the machine state!
7885 */
7886void Machine::ensureNoStateDependencies()
7887{
7888 AssertReturnVoid(isWriteLockOnCurrentThread());
7889
7890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7891
7892 /* Wait for all state dependents if necessary */
7893 if (mData->mMachineStateDeps != 0)
7894 {
7895 /* lazy semaphore creation */
7896 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
7897 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
7898
7899 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
7900 mData->mMachineStateDeps));
7901
7902 ++mData->mMachineStateChangePending;
7903
7904 /* reset the semaphore before waiting, the last dependent will signal
7905 * it */
7906 RTSemEventMultiReset(mData->mMachineStateDepsSem);
7907
7908 alock.release();
7909
7910 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
7911
7912 alock.acquire();
7913
7914 -- mData->mMachineStateChangePending;
7915 }
7916}
7917
7918/**
7919 * Changes the machine state and informs callbacks.
7920 *
7921 * This method is not intended to fail so it either returns S_OK or asserts (and
7922 * returns a failure).
7923 *
7924 * @note Locks this object for writing.
7925 */
7926HRESULT Machine::setMachineState(MachineState_T aMachineState)
7927{
7928 LogFlowThisFuncEnter();
7929 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
7930
7931 AutoCaller autoCaller(this);
7932 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7933
7934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7935
7936 /* wait for state dependents to drop to zero */
7937 ensureNoStateDependencies();
7938
7939 if (mData->mMachineState != aMachineState)
7940 {
7941 mData->mMachineState = aMachineState;
7942
7943 RTTimeNow(&mData->mLastStateChange);
7944
7945 mParent->onMachineStateChange(mData->mUuid, aMachineState);
7946 }
7947
7948 LogFlowThisFuncLeave();
7949 return S_OK;
7950}
7951
7952/**
7953 * Searches for a shared folder with the given logical name
7954 * in the collection of shared folders.
7955 *
7956 * @param aName logical name of the shared folder
7957 * @param aSharedFolder where to return the found object
7958 * @param aSetError whether to set the error info if the folder is
7959 * not found
7960 * @return
7961 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
7962 *
7963 * @note
7964 * must be called from under the object's lock!
7965 */
7966HRESULT Machine::findSharedFolder(const Utf8Str &aName,
7967 ComObjPtr<SharedFolder> &aSharedFolder,
7968 bool aSetError /* = false */)
7969{
7970 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
7971 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7972 it != mHWData->mSharedFolders.end();
7973 ++it)
7974 {
7975 SharedFolder *pSF = *it;
7976 AutoCaller autoCaller(pSF);
7977 if (pSF->getName() == aName)
7978 {
7979 aSharedFolder = pSF;
7980 rc = S_OK;
7981 break;
7982 }
7983 }
7984
7985 if (aSetError && FAILED(rc))
7986 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
7987
7988 return rc;
7989}
7990
7991/**
7992 * Initializes all machine instance data from the given settings structures
7993 * from XML. The exception is the machine UUID which needs special handling
7994 * depending on the caller's use case, so the caller needs to set that herself.
7995 *
7996 * This gets called in several contexts during machine initialization:
7997 *
7998 * -- When machine XML exists on disk already and needs to be loaded into memory,
7999 * for example, from registeredInit() to load all registered machines on
8000 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8001 * attached to the machine should be part of some media registry already.
8002 *
8003 * -- During OVF import, when a machine config has been constructed from an
8004 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8005 * ensure that the media listed as attachments in the config (which have
8006 * been imported from the OVF) receive the correct registry ID.
8007 *
8008 * -- During VM cloning.
8009 *
8010 * @param config Machine settings from XML.
8011 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8012 * @return
8013 */
8014HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8015 const Guid *puuidRegistry)
8016{
8017 // copy name, description, OS type, teleporter, UTC etc.
8018 mUserData->s = config.machineUserData;
8019
8020 // look up the object by Id to check it is valid
8021 ComPtr<IGuestOSType> guestOSType;
8022 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8023 guestOSType.asOutParam());
8024 if (FAILED(rc)) return rc;
8025
8026 // stateFile (optional)
8027 if (config.strStateFile.isEmpty())
8028 mSSData->strStateFilePath.setNull();
8029 else
8030 {
8031 Utf8Str stateFilePathFull(config.strStateFile);
8032 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8033 if (RT_FAILURE(vrc))
8034 return setError(E_FAIL,
8035 tr("Invalid saved state file path '%s' (%Rrc)"),
8036 config.strStateFile.c_str(),
8037 vrc);
8038 mSSData->strStateFilePath = stateFilePathFull;
8039 }
8040
8041 // snapshot folder needs special processing so set it again
8042 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8043 if (FAILED(rc)) return rc;
8044
8045 /* Copy the extra data items (Not in any case config is already the same as
8046 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8047 * make sure the extra data map is copied). */
8048 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8049
8050 /* currentStateModified (optional, default is true) */
8051 mData->mCurrentStateModified = config.fCurrentStateModified;
8052
8053 mData->mLastStateChange = config.timeLastStateChange;
8054
8055 /*
8056 * note: all mUserData members must be assigned prior this point because
8057 * we need to commit changes in order to let mUserData be shared by all
8058 * snapshot machine instances.
8059 */
8060 mUserData.commitCopy();
8061
8062 // machine registry, if present (must be loaded before snapshots)
8063 if (config.canHaveOwnMediaRegistry())
8064 {
8065 // determine machine folder
8066 Utf8Str strMachineFolder = getSettingsFileFull();
8067 strMachineFolder.stripFilename();
8068 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8069 config.mediaRegistry,
8070 strMachineFolder);
8071 if (FAILED(rc)) return rc;
8072 }
8073
8074 /* Snapshot node (optional) */
8075 size_t cRootSnapshots;
8076 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8077 {
8078 // there must be only one root snapshot
8079 Assert(cRootSnapshots == 1);
8080
8081 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8082
8083 rc = loadSnapshot(snap,
8084 config.uuidCurrentSnapshot,
8085 NULL); // no parent == first snapshot
8086 if (FAILED(rc)) return rc;
8087 }
8088
8089 // hardware data
8090 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8091 if (FAILED(rc)) return rc;
8092
8093 // load storage controllers
8094 rc = loadStorageControllers(config.storageMachine,
8095 puuidRegistry,
8096 NULL /* puuidSnapshot */);
8097 if (FAILED(rc)) return rc;
8098
8099 /*
8100 * NOTE: the assignment below must be the last thing to do,
8101 * otherwise it will be not possible to change the settings
8102 * somewhere in the code above because all setters will be
8103 * blocked by checkStateDependency(MutableStateDep).
8104 */
8105
8106 /* set the machine state to Aborted or Saved when appropriate */
8107 if (config.fAborted)
8108 {
8109 mSSData->strStateFilePath.setNull();
8110
8111 /* no need to use setMachineState() during init() */
8112 mData->mMachineState = MachineState_Aborted;
8113 }
8114 else if (!mSSData->strStateFilePath.isEmpty())
8115 {
8116 /* no need to use setMachineState() during init() */
8117 mData->mMachineState = MachineState_Saved;
8118 }
8119
8120 // after loading settings, we are no longer different from the XML on disk
8121 mData->flModifications = 0;
8122
8123 return S_OK;
8124}
8125
8126/**
8127 * Recursively loads all snapshots starting from the given.
8128 *
8129 * @param aNode <Snapshot> node.
8130 * @param aCurSnapshotId Current snapshot ID from the settings file.
8131 * @param aParentSnapshot Parent snapshot.
8132 */
8133HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8134 const Guid &aCurSnapshotId,
8135 Snapshot *aParentSnapshot)
8136{
8137 AssertReturn(!isSnapshotMachine(), E_FAIL);
8138 AssertReturn(!isSessionMachine(), E_FAIL);
8139
8140 HRESULT rc = S_OK;
8141
8142 Utf8Str strStateFile;
8143 if (!data.strStateFile.isEmpty())
8144 {
8145 /* optional */
8146 strStateFile = data.strStateFile;
8147 int vrc = calculateFullPath(strStateFile, strStateFile);
8148 if (RT_FAILURE(vrc))
8149 return setError(E_FAIL,
8150 tr("Invalid saved state file path '%s' (%Rrc)"),
8151 strStateFile.c_str(),
8152 vrc);
8153 }
8154
8155 /* create a snapshot machine object */
8156 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8157 pSnapshotMachine.createObject();
8158 rc = pSnapshotMachine->initFromSettings(this,
8159 data.hardware,
8160 &data.debugging,
8161 &data.autostart,
8162 data.storage,
8163 data.uuid.ref(),
8164 strStateFile);
8165 if (FAILED(rc)) return rc;
8166
8167 /* create a snapshot object */
8168 ComObjPtr<Snapshot> pSnapshot;
8169 pSnapshot.createObject();
8170 /* initialize the snapshot */
8171 rc = pSnapshot->init(mParent, // VirtualBox object
8172 data.uuid,
8173 data.strName,
8174 data.strDescription,
8175 data.timestamp,
8176 pSnapshotMachine,
8177 aParentSnapshot);
8178 if (FAILED(rc)) return rc;
8179
8180 /* memorize the first snapshot if necessary */
8181 if (!mData->mFirstSnapshot)
8182 mData->mFirstSnapshot = pSnapshot;
8183
8184 /* memorize the current snapshot when appropriate */
8185 if ( !mData->mCurrentSnapshot
8186 && pSnapshot->getId() == aCurSnapshotId
8187 )
8188 mData->mCurrentSnapshot = pSnapshot;
8189
8190 // now create the children
8191 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8192 it != data.llChildSnapshots.end();
8193 ++it)
8194 {
8195 const settings::Snapshot &childData = *it;
8196 // recurse
8197 rc = loadSnapshot(childData,
8198 aCurSnapshotId,
8199 pSnapshot); // parent = the one we created above
8200 if (FAILED(rc)) return rc;
8201 }
8202
8203 return rc;
8204}
8205
8206/**
8207 * Loads settings into mHWData.
8208 *
8209 * @param data Reference to the hardware settings.
8210 * @param pDbg Pointer to the debugging settings.
8211 * @param pAutostart Pointer to the autostart settings.
8212 */
8213HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8214 const settings::Autostart *pAutostart)
8215{
8216 AssertReturn(!isSessionMachine(), E_FAIL);
8217
8218 HRESULT rc = S_OK;
8219
8220 try
8221 {
8222 /* The hardware version attribute (optional). */
8223 mHWData->mHWVersion = data.strVersion;
8224 mHWData->mHardwareUUID = data.uuid;
8225
8226 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8227 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8228 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8229 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8230 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8231 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8232 mHWData->mPAEEnabled = data.fPAE;
8233 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8234
8235 mHWData->mCPUCount = data.cCPUs;
8236 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8237 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8238
8239 // cpu
8240 if (mHWData->mCPUHotPlugEnabled)
8241 {
8242 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8243 it != data.llCpus.end();
8244 ++it)
8245 {
8246 const settings::Cpu &cpu = *it;
8247
8248 mHWData->mCPUAttached[cpu.ulId] = true;
8249 }
8250 }
8251
8252 // cpuid leafs
8253 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8254 it != data.llCpuIdLeafs.end();
8255 ++it)
8256 {
8257 const settings::CpuIdLeaf &leaf = *it;
8258
8259 switch (leaf.ulId)
8260 {
8261 case 0x0:
8262 case 0x1:
8263 case 0x2:
8264 case 0x3:
8265 case 0x4:
8266 case 0x5:
8267 case 0x6:
8268 case 0x7:
8269 case 0x8:
8270 case 0x9:
8271 case 0xA:
8272 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8273 break;
8274
8275 case 0x80000000:
8276 case 0x80000001:
8277 case 0x80000002:
8278 case 0x80000003:
8279 case 0x80000004:
8280 case 0x80000005:
8281 case 0x80000006:
8282 case 0x80000007:
8283 case 0x80000008:
8284 case 0x80000009:
8285 case 0x8000000A:
8286 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8287 break;
8288
8289 default:
8290 /* just ignore */
8291 break;
8292 }
8293 }
8294
8295 mHWData->mMemorySize = data.ulMemorySizeMB;
8296 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8297
8298 // boot order
8299 for (size_t i = 0;
8300 i < RT_ELEMENTS(mHWData->mBootOrder);
8301 i++)
8302 {
8303 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8304 if (it == data.mapBootOrder.end())
8305 mHWData->mBootOrder[i] = DeviceType_Null;
8306 else
8307 mHWData->mBootOrder[i] = it->second;
8308 }
8309
8310 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8311 mHWData->mMonitorCount = data.cMonitors;
8312 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8313 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8314 mHWData->mFirmwareType = data.firmwareType;
8315 mHWData->mPointingHidType = data.pointingHidType;
8316 mHWData->mKeyboardHidType = data.keyboardHidType;
8317 mHWData->mChipsetType = data.chipsetType;
8318 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8319 mHWData->mHpetEnabled = data.fHpetEnabled;
8320
8321 /* VRDEServer */
8322 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8323 if (FAILED(rc)) return rc;
8324
8325 /* BIOS */
8326 rc = mBIOSSettings->loadSettings(data.biosSettings);
8327 if (FAILED(rc)) return rc;
8328
8329 // Bandwidth control (must come before network adapters)
8330 rc = mBandwidthControl->loadSettings(data.ioSettings);
8331 if (FAILED(rc)) return rc;
8332
8333 /* USB Controller */
8334 rc = mUSBController->loadSettings(data.usbController);
8335 if (FAILED(rc)) return rc;
8336
8337 // network adapters
8338 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8339 uint32_t oldCount = mNetworkAdapters.size();
8340 if (newCount > oldCount)
8341 {
8342 mNetworkAdapters.resize(newCount);
8343 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8344 {
8345 unconst(mNetworkAdapters[slot]).createObject();
8346 mNetworkAdapters[slot]->init(this, slot);
8347 }
8348 }
8349 else if (newCount < oldCount)
8350 mNetworkAdapters.resize(newCount);
8351 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8352 it != data.llNetworkAdapters.end();
8353 ++it)
8354 {
8355 const settings::NetworkAdapter &nic = *it;
8356
8357 /* slot unicity is guaranteed by XML Schema */
8358 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8359 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8360 if (FAILED(rc)) return rc;
8361 }
8362
8363 // serial ports
8364 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8365 it != data.llSerialPorts.end();
8366 ++it)
8367 {
8368 const settings::SerialPort &s = *it;
8369
8370 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8371 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8372 if (FAILED(rc)) return rc;
8373 }
8374
8375 // parallel ports (optional)
8376 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8377 it != data.llParallelPorts.end();
8378 ++it)
8379 {
8380 const settings::ParallelPort &p = *it;
8381
8382 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8383 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8384 if (FAILED(rc)) return rc;
8385 }
8386
8387 /* AudioAdapter */
8388 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8389 if (FAILED(rc)) return rc;
8390
8391 /* Shared folders */
8392 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8393 it != data.llSharedFolders.end();
8394 ++it)
8395 {
8396 const settings::SharedFolder &sf = *it;
8397
8398 ComObjPtr<SharedFolder> sharedFolder;
8399 /* Check for double entries. Not allowed! */
8400 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8401 if (SUCCEEDED(rc))
8402 return setError(VBOX_E_OBJECT_IN_USE,
8403 tr("Shared folder named '%s' already exists"),
8404 sf.strName.c_str());
8405
8406 /* Create the new shared folder. Don't break on error. This will be
8407 * reported when the machine starts. */
8408 sharedFolder.createObject();
8409 rc = sharedFolder->init(getMachine(),
8410 sf.strName,
8411 sf.strHostPath,
8412 RT_BOOL(sf.fWritable),
8413 RT_BOOL(sf.fAutoMount),
8414 false /* fFailOnError */);
8415 if (FAILED(rc)) return rc;
8416 mHWData->mSharedFolders.push_back(sharedFolder);
8417 }
8418
8419 // Clipboard
8420 mHWData->mClipboardMode = data.clipboardMode;
8421
8422 // guest settings
8423 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8424
8425 // IO settings
8426 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
8427 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
8428
8429 // Host PCI devices
8430 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8431 it != data.pciAttachments.end();
8432 ++it)
8433 {
8434 const settings::HostPciDeviceAttachment &hpda = *it;
8435 ComObjPtr<PciDeviceAttachment> pda;
8436
8437 pda.createObject();
8438 pda->loadSettings(this, hpda);
8439 mHWData->mPciDeviceAssignments.push_back(pda);
8440 }
8441
8442 /*
8443 * (The following isn't really real hardware, but it lives in HWData
8444 * for reasons of convenience.)
8445 */
8446
8447#ifdef VBOX_WITH_GUEST_PROPS
8448 /* Guest properties (optional) */
8449 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8450 it != data.llGuestProperties.end();
8451 ++it)
8452 {
8453 const settings::GuestProperty &prop = *it;
8454 uint32_t fFlags = guestProp::NILFLAG;
8455 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8456 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8457 mHWData->mGuestProperties.push_back(property);
8458 }
8459
8460 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8461#endif /* VBOX_WITH_GUEST_PROPS defined */
8462
8463 rc = loadDebugging(pDbg);
8464 if (FAILED(rc))
8465 return rc;
8466
8467 mHWData->mAutostart = *pAutostart;
8468 }
8469 catch(std::bad_alloc &)
8470 {
8471 return E_OUTOFMEMORY;
8472 }
8473
8474 AssertComRC(rc);
8475 return rc;
8476}
8477
8478/**
8479 * Called from Machine::loadHardware() to load the debugging settings of the
8480 * machine.
8481 *
8482 * @param pDbg Pointer to the settings.
8483 */
8484HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8485{
8486 mHWData->mDebugging = *pDbg;
8487 /* no more processing currently required, this will probably change. */
8488 return S_OK;
8489}
8490
8491/**
8492 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8493 *
8494 * @param data
8495 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8496 * @param puuidSnapshot
8497 * @return
8498 */
8499HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8500 const Guid *puuidRegistry,
8501 const Guid *puuidSnapshot)
8502{
8503 AssertReturn(!isSessionMachine(), E_FAIL);
8504
8505 HRESULT rc = S_OK;
8506
8507 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8508 it != data.llStorageControllers.end();
8509 ++it)
8510 {
8511 const settings::StorageController &ctlData = *it;
8512
8513 ComObjPtr<StorageController> pCtl;
8514 /* Try to find one with the name first. */
8515 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8516 if (SUCCEEDED(rc))
8517 return setError(VBOX_E_OBJECT_IN_USE,
8518 tr("Storage controller named '%s' already exists"),
8519 ctlData.strName.c_str());
8520
8521 pCtl.createObject();
8522 rc = pCtl->init(this,
8523 ctlData.strName,
8524 ctlData.storageBus,
8525 ctlData.ulInstance,
8526 ctlData.fBootable);
8527 if (FAILED(rc)) return rc;
8528
8529 mStorageControllers->push_back(pCtl);
8530
8531 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8532 if (FAILED(rc)) return rc;
8533
8534 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8535 if (FAILED(rc)) return rc;
8536
8537 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8538 if (FAILED(rc)) return rc;
8539
8540 /* Set IDE emulation settings (only for AHCI controller). */
8541 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8542 {
8543 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8544 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8545 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8546 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8547 )
8548 return rc;
8549 }
8550
8551 /* Load the attached devices now. */
8552 rc = loadStorageDevices(pCtl,
8553 ctlData,
8554 puuidRegistry,
8555 puuidSnapshot);
8556 if (FAILED(rc)) return rc;
8557 }
8558
8559 return S_OK;
8560}
8561
8562/**
8563 * Called from loadStorageControllers for a controller's devices.
8564 *
8565 * @param aStorageController
8566 * @param data
8567 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8568 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8569 * @return
8570 */
8571HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8572 const settings::StorageController &data,
8573 const Guid *puuidRegistry,
8574 const Guid *puuidSnapshot)
8575{
8576 HRESULT rc = S_OK;
8577
8578 /* paranoia: detect duplicate attachments */
8579 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8580 it != data.llAttachedDevices.end();
8581 ++it)
8582 {
8583 const settings::AttachedDevice &ad = *it;
8584
8585 for (settings::AttachedDevicesList::const_iterator it2 = it;
8586 it2 != data.llAttachedDevices.end();
8587 ++it2)
8588 {
8589 if (it == it2)
8590 continue;
8591
8592 const settings::AttachedDevice &ad2 = *it2;
8593
8594 if ( ad.lPort == ad2.lPort
8595 && ad.lDevice == ad2.lDevice)
8596 {
8597 return setError(E_FAIL,
8598 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8599 aStorageController->getName().c_str(),
8600 ad.lPort,
8601 ad.lDevice,
8602 mUserData->s.strName.c_str());
8603 }
8604 }
8605 }
8606
8607 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8608 it != data.llAttachedDevices.end();
8609 ++it)
8610 {
8611 const settings::AttachedDevice &dev = *it;
8612 ComObjPtr<Medium> medium;
8613
8614 switch (dev.deviceType)
8615 {
8616 case DeviceType_Floppy:
8617 case DeviceType_DVD:
8618 if (dev.strHostDriveSrc.isNotEmpty())
8619 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8620 else
8621 rc = mParent->findRemoveableMedium(dev.deviceType,
8622 dev.uuid,
8623 false /* fRefresh */,
8624 false /* aSetError */,
8625 medium);
8626 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8627 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8628 rc = S_OK;
8629 break;
8630
8631 case DeviceType_HardDisk:
8632 {
8633 /* find a hard disk by UUID */
8634 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8635 if (FAILED(rc))
8636 {
8637 if (isSnapshotMachine())
8638 {
8639 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8640 // so the user knows that the bad disk is in a snapshot somewhere
8641 com::ErrorInfo info;
8642 return setError(E_FAIL,
8643 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8644 puuidSnapshot->raw(),
8645 info.getText().raw());
8646 }
8647 else
8648 return rc;
8649 }
8650
8651 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8652
8653 if (medium->getType() == MediumType_Immutable)
8654 {
8655 if (isSnapshotMachine())
8656 return setError(E_FAIL,
8657 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8658 "of the virtual machine '%s' ('%s')"),
8659 medium->getLocationFull().c_str(),
8660 dev.uuid.raw(),
8661 puuidSnapshot->raw(),
8662 mUserData->s.strName.c_str(),
8663 mData->m_strConfigFileFull.c_str());
8664
8665 return setError(E_FAIL,
8666 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8667 medium->getLocationFull().c_str(),
8668 dev.uuid.raw(),
8669 mUserData->s.strName.c_str(),
8670 mData->m_strConfigFileFull.c_str());
8671 }
8672
8673 if (medium->getType() == MediumType_MultiAttach)
8674 {
8675 if (isSnapshotMachine())
8676 return setError(E_FAIL,
8677 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8678 "of the virtual machine '%s' ('%s')"),
8679 medium->getLocationFull().c_str(),
8680 dev.uuid.raw(),
8681 puuidSnapshot->raw(),
8682 mUserData->s.strName.c_str(),
8683 mData->m_strConfigFileFull.c_str());
8684
8685 return setError(E_FAIL,
8686 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8687 medium->getLocationFull().c_str(),
8688 dev.uuid.raw(),
8689 mUserData->s.strName.c_str(),
8690 mData->m_strConfigFileFull.c_str());
8691 }
8692
8693 if ( !isSnapshotMachine()
8694 && medium->getChildren().size() != 0
8695 )
8696 return setError(E_FAIL,
8697 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8698 "because it has %d differencing child hard disks"),
8699 medium->getLocationFull().c_str(),
8700 dev.uuid.raw(),
8701 mUserData->s.strName.c_str(),
8702 mData->m_strConfigFileFull.c_str(),
8703 medium->getChildren().size());
8704
8705 if (findAttachment(mMediaData->mAttachments,
8706 medium))
8707 return setError(E_FAIL,
8708 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8709 medium->getLocationFull().c_str(),
8710 dev.uuid.raw(),
8711 mUserData->s.strName.c_str(),
8712 mData->m_strConfigFileFull.c_str());
8713
8714 break;
8715 }
8716
8717 default:
8718 return setError(E_FAIL,
8719 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8720 medium->getLocationFull().c_str(),
8721 mUserData->s.strName.c_str(),
8722 mData->m_strConfigFileFull.c_str());
8723 }
8724
8725 if (FAILED(rc))
8726 break;
8727
8728 /* Bandwidth groups are loaded at this point. */
8729 ComObjPtr<BandwidthGroup> pBwGroup;
8730
8731 if (!dev.strBwGroup.isEmpty())
8732 {
8733 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8734 if (FAILED(rc))
8735 return setError(E_FAIL,
8736 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8737 medium->getLocationFull().c_str(),
8738 dev.strBwGroup.c_str(),
8739 mUserData->s.strName.c_str(),
8740 mData->m_strConfigFileFull.c_str());
8741 pBwGroup->reference();
8742 }
8743
8744 const Bstr controllerName = aStorageController->getName();
8745 ComObjPtr<MediumAttachment> pAttachment;
8746 pAttachment.createObject();
8747 rc = pAttachment->init(this,
8748 medium,
8749 controllerName,
8750 dev.lPort,
8751 dev.lDevice,
8752 dev.deviceType,
8753 false,
8754 dev.fPassThrough,
8755 dev.fTempEject,
8756 dev.fNonRotational,
8757 dev.fDiscard,
8758 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8759 if (FAILED(rc)) break;
8760
8761 /* associate the medium with this machine and snapshot */
8762 if (!medium.isNull())
8763 {
8764 AutoCaller medCaller(medium);
8765 if (FAILED(medCaller.rc())) return medCaller.rc();
8766 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8767
8768 if (isSnapshotMachine())
8769 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8770 else
8771 rc = medium->addBackReference(mData->mUuid);
8772 /* If the medium->addBackReference fails it sets an appropriate
8773 * error message, so no need to do any guesswork here. */
8774
8775 if (puuidRegistry)
8776 // caller wants registry ID to be set on all attached media (OVF import case)
8777 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8778 }
8779
8780 if (FAILED(rc))
8781 break;
8782
8783 /* back up mMediaData to let registeredInit() properly rollback on failure
8784 * (= limited accessibility) */
8785 setModified(IsModified_Storage);
8786 mMediaData.backup();
8787 mMediaData->mAttachments.push_back(pAttachment);
8788 }
8789
8790 return rc;
8791}
8792
8793/**
8794 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8795 *
8796 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8797 * @param aSnapshot where to return the found snapshot
8798 * @param aSetError true to set extended error info on failure
8799 */
8800HRESULT Machine::findSnapshotById(const Guid &aId,
8801 ComObjPtr<Snapshot> &aSnapshot,
8802 bool aSetError /* = false */)
8803{
8804 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8805
8806 if (!mData->mFirstSnapshot)
8807 {
8808 if (aSetError)
8809 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8810 return E_FAIL;
8811 }
8812
8813 if (aId.isEmpty())
8814 aSnapshot = mData->mFirstSnapshot;
8815 else
8816 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8817
8818 if (!aSnapshot)
8819 {
8820 if (aSetError)
8821 return setError(E_FAIL,
8822 tr("Could not find a snapshot with UUID {%s}"),
8823 aId.toString().c_str());
8824 return E_FAIL;
8825 }
8826
8827 return S_OK;
8828}
8829
8830/**
8831 * Returns the snapshot with the given name or fails of no such snapshot.
8832 *
8833 * @param aName snapshot name to find
8834 * @param aSnapshot where to return the found snapshot
8835 * @param aSetError true to set extended error info on failure
8836 */
8837HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8838 ComObjPtr<Snapshot> &aSnapshot,
8839 bool aSetError /* = false */)
8840{
8841 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8842
8843 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8844
8845 if (!mData->mFirstSnapshot)
8846 {
8847 if (aSetError)
8848 return setError(VBOX_E_OBJECT_NOT_FOUND,
8849 tr("This machine does not have any snapshots"));
8850 return VBOX_E_OBJECT_NOT_FOUND;
8851 }
8852
8853 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8854
8855 if (!aSnapshot)
8856 {
8857 if (aSetError)
8858 return setError(VBOX_E_OBJECT_NOT_FOUND,
8859 tr("Could not find a snapshot named '%s'"), strName.c_str());
8860 return VBOX_E_OBJECT_NOT_FOUND;
8861 }
8862
8863 return S_OK;
8864}
8865
8866/**
8867 * Returns a storage controller object with the given name.
8868 *
8869 * @param aName storage controller name to find
8870 * @param aStorageController where to return the found storage controller
8871 * @param aSetError true to set extended error info on failure
8872 */
8873HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8874 ComObjPtr<StorageController> &aStorageController,
8875 bool aSetError /* = false */)
8876{
8877 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8878
8879 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8880 it != mStorageControllers->end();
8881 ++it)
8882 {
8883 if ((*it)->getName() == aName)
8884 {
8885 aStorageController = (*it);
8886 return S_OK;
8887 }
8888 }
8889
8890 if (aSetError)
8891 return setError(VBOX_E_OBJECT_NOT_FOUND,
8892 tr("Could not find a storage controller named '%s'"),
8893 aName.c_str());
8894 return VBOX_E_OBJECT_NOT_FOUND;
8895}
8896
8897HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
8898 MediaData::AttachmentList &atts)
8899{
8900 AutoCaller autoCaller(this);
8901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8902
8903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8904
8905 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
8906 it != mMediaData->mAttachments.end();
8907 ++it)
8908 {
8909 const ComObjPtr<MediumAttachment> &pAtt = *it;
8910
8911 // should never happen, but deal with NULL pointers in the list.
8912 AssertStmt(!pAtt.isNull(), continue);
8913
8914 // getControllerName() needs caller+read lock
8915 AutoCaller autoAttCaller(pAtt);
8916 if (FAILED(autoAttCaller.rc()))
8917 {
8918 atts.clear();
8919 return autoAttCaller.rc();
8920 }
8921 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
8922
8923 if (pAtt->getControllerName() == aName)
8924 atts.push_back(pAtt);
8925 }
8926
8927 return S_OK;
8928}
8929
8930/**
8931 * Helper for #saveSettings. Cares about renaming the settings directory and
8932 * file if the machine name was changed and about creating a new settings file
8933 * if this is a new machine.
8934 *
8935 * @note Must be never called directly but only from #saveSettings().
8936 */
8937HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
8938{
8939 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8940
8941 HRESULT rc = S_OK;
8942
8943 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
8944
8945 /// @todo need to handle primary group change, too
8946
8947 /* attempt to rename the settings file if machine name is changed */
8948 if ( mUserData->s.fNameSync
8949 && mUserData.isBackedUp()
8950 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
8951 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
8952 )
8953 {
8954 bool dirRenamed = false;
8955 bool fileRenamed = false;
8956
8957 Utf8Str configFile, newConfigFile;
8958 Utf8Str configFilePrev, newConfigFilePrev;
8959 Utf8Str configDir, newConfigDir;
8960
8961 do
8962 {
8963 int vrc = VINF_SUCCESS;
8964
8965 Utf8Str name = mUserData.backedUpData()->s.strName;
8966 Utf8Str newName = mUserData->s.strName;
8967 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
8968 if (group == "/")
8969 group.setNull();
8970 Utf8Str newGroup = mUserData->s.llGroups.front();
8971 if (newGroup == "/")
8972 newGroup.setNull();
8973
8974 configFile = mData->m_strConfigFileFull;
8975
8976 /* first, rename the directory if it matches the group and machine name */
8977 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
8978 group.c_str(), RTPATH_DELIMITER, name.c_str());
8979 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
8980 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
8981 configDir = configFile;
8982 configDir.stripFilename();
8983 newConfigDir = configDir;
8984 if ( configDir.length() >= groupPlusName.length()
8985 && configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).equals(groupPlusName.c_str()))
8986 {
8987 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
8988 Utf8Str newConfigBaseDir(newConfigDir);
8989 newConfigDir.append(newGroupPlusName);
8990 /* new dir and old dir cannot be equal here because of 'if'
8991 * above and because name != newName */
8992 Assert(configDir != newConfigDir);
8993 if (!fSettingsFileIsNew)
8994 {
8995 /* perform real rename only if the machine is not new */
8996 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
8997 if ( vrc == VERR_FILE_NOT_FOUND
8998 || vrc == VERR_PATH_NOT_FOUND)
8999 {
9000 /* create the parent directory, then retry renaming */
9001 Utf8Str parent(newConfigDir);
9002 parent.stripFilename();
9003 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9004 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9005 }
9006 if (RT_FAILURE(vrc))
9007 {
9008 rc = setError(E_FAIL,
9009 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9010 configDir.c_str(),
9011 newConfigDir.c_str(),
9012 vrc);
9013 break;
9014 }
9015 /* delete subdirectories which are no longer needed */
9016 Utf8Str dir(configDir);
9017 dir.stripFilename();
9018 while (dir != newConfigBaseDir && dir != ".")
9019 {
9020 vrc = RTDirRemove(dir.c_str());
9021 if (RT_FAILURE(vrc))
9022 break;
9023 dir.stripFilename();
9024 }
9025 dirRenamed = true;
9026 }
9027 }
9028
9029 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9030 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9031
9032 /* then try to rename the settings file itself */
9033 if (newConfigFile != configFile)
9034 {
9035 /* get the path to old settings file in renamed directory */
9036 configFile = Utf8StrFmt("%s%c%s",
9037 newConfigDir.c_str(),
9038 RTPATH_DELIMITER,
9039 RTPathFilename(configFile.c_str()));
9040 if (!fSettingsFileIsNew)
9041 {
9042 /* perform real rename only if the machine is not new */
9043 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9044 if (RT_FAILURE(vrc))
9045 {
9046 rc = setError(E_FAIL,
9047 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9048 configFile.c_str(),
9049 newConfigFile.c_str(),
9050 vrc);
9051 break;
9052 }
9053 fileRenamed = true;
9054 configFilePrev = configFile;
9055 configFilePrev += "-prev";
9056 newConfigFilePrev = newConfigFile;
9057 newConfigFilePrev += "-prev";
9058 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9059 }
9060 }
9061
9062 // update m_strConfigFileFull amd mConfigFile
9063 mData->m_strConfigFileFull = newConfigFile;
9064 // compute the relative path too
9065 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9066
9067 // store the old and new so that VirtualBox::saveSettings() can update
9068 // the media registry
9069 if ( mData->mRegistered
9070 && configDir != newConfigDir)
9071 {
9072 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9073
9074 if (pfNeedsGlobalSaveSettings)
9075 *pfNeedsGlobalSaveSettings = true;
9076 }
9077
9078 // in the saved state file path, replace the old directory with the new directory
9079 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9080 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9081
9082 // and do the same thing for the saved state file paths of all the online snapshots
9083 if (mData->mFirstSnapshot)
9084 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9085 newConfigDir.c_str());
9086 }
9087 while (0);
9088
9089 if (FAILED(rc))
9090 {
9091 /* silently try to rename everything back */
9092 if (fileRenamed)
9093 {
9094 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9095 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9096 }
9097 if (dirRenamed)
9098 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9099 }
9100
9101 if (FAILED(rc)) return rc;
9102 }
9103
9104 if (fSettingsFileIsNew)
9105 {
9106 /* create a virgin config file */
9107 int vrc = VINF_SUCCESS;
9108
9109 /* ensure the settings directory exists */
9110 Utf8Str path(mData->m_strConfigFileFull);
9111 path.stripFilename();
9112 if (!RTDirExists(path.c_str()))
9113 {
9114 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9115 if (RT_FAILURE(vrc))
9116 {
9117 return setError(E_FAIL,
9118 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9119 path.c_str(),
9120 vrc);
9121 }
9122 }
9123
9124 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9125 path = Utf8Str(mData->m_strConfigFileFull);
9126 RTFILE f = NIL_RTFILE;
9127 vrc = RTFileOpen(&f, path.c_str(),
9128 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9129 if (RT_FAILURE(vrc))
9130 return setError(E_FAIL,
9131 tr("Could not create the settings file '%s' (%Rrc)"),
9132 path.c_str(),
9133 vrc);
9134 RTFileClose(f);
9135 }
9136
9137 return rc;
9138}
9139
9140/**
9141 * Saves and commits machine data, user data and hardware data.
9142 *
9143 * Note that on failure, the data remains uncommitted.
9144 *
9145 * @a aFlags may combine the following flags:
9146 *
9147 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9148 * Used when saving settings after an operation that makes them 100%
9149 * correspond to the settings from the current snapshot.
9150 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9151 * #isReallyModified() returns false. This is necessary for cases when we
9152 * change machine data directly, not through the backup()/commit() mechanism.
9153 * - SaveS_Force: settings will be saved without doing a deep compare of the
9154 * settings structures. This is used when this is called because snapshots
9155 * have changed to avoid the overhead of the deep compare.
9156 *
9157 * @note Must be called from under this object's write lock. Locks children for
9158 * writing.
9159 *
9160 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9161 * initialized to false and that will be set to true by this function if
9162 * the caller must invoke VirtualBox::saveSettings() because the global
9163 * settings have changed. This will happen if a machine rename has been
9164 * saved and the global machine and media registries will therefore need
9165 * updating.
9166 */
9167HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9168 int aFlags /*= 0*/)
9169{
9170 LogFlowThisFuncEnter();
9171
9172 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9173
9174 /* make sure child objects are unable to modify the settings while we are
9175 * saving them */
9176 ensureNoStateDependencies();
9177
9178 AssertReturn(!isSnapshotMachine(),
9179 E_FAIL);
9180
9181 HRESULT rc = S_OK;
9182 bool fNeedsWrite = false;
9183
9184 /* First, prepare to save settings. It will care about renaming the
9185 * settings directory and file if the machine name was changed and about
9186 * creating a new settings file if this is a new machine. */
9187 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9188 if (FAILED(rc)) return rc;
9189
9190 // keep a pointer to the current settings structures
9191 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9192 settings::MachineConfigFile *pNewConfig = NULL;
9193
9194 try
9195 {
9196 // make a fresh one to have everyone write stuff into
9197 pNewConfig = new settings::MachineConfigFile(NULL);
9198 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9199
9200 // now go and copy all the settings data from COM to the settings structures
9201 // (this calles saveSettings() on all the COM objects in the machine)
9202 copyMachineDataToSettings(*pNewConfig);
9203
9204 if (aFlags & SaveS_ResetCurStateModified)
9205 {
9206 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9207 mData->mCurrentStateModified = FALSE;
9208 fNeedsWrite = true; // always, no need to compare
9209 }
9210 else if (aFlags & SaveS_Force)
9211 {
9212 fNeedsWrite = true; // always, no need to compare
9213 }
9214 else
9215 {
9216 if (!mData->mCurrentStateModified)
9217 {
9218 // do a deep compare of the settings that we just saved with the settings
9219 // previously stored in the config file; this invokes MachineConfigFile::operator==
9220 // which does a deep compare of all the settings, which is expensive but less expensive
9221 // than writing out XML in vain
9222 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9223
9224 // could still be modified if any settings changed
9225 mData->mCurrentStateModified = fAnySettingsChanged;
9226
9227 fNeedsWrite = fAnySettingsChanged;
9228 }
9229 else
9230 fNeedsWrite = true;
9231 }
9232
9233 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9234
9235 if (fNeedsWrite)
9236 // now spit it all out!
9237 pNewConfig->write(mData->m_strConfigFileFull);
9238
9239 mData->pMachineConfigFile = pNewConfig;
9240 delete pOldConfig;
9241 commit();
9242
9243 // after saving settings, we are no longer different from the XML on disk
9244 mData->flModifications = 0;
9245 }
9246 catch (HRESULT err)
9247 {
9248 // we assume that error info is set by the thrower
9249 rc = err;
9250
9251 // restore old config
9252 delete pNewConfig;
9253 mData->pMachineConfigFile = pOldConfig;
9254 }
9255 catch (...)
9256 {
9257 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9258 }
9259
9260 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9261 {
9262 /* Fire the data change event, even on failure (since we've already
9263 * committed all data). This is done only for SessionMachines because
9264 * mutable Machine instances are always not registered (i.e. private
9265 * to the client process that creates them) and thus don't need to
9266 * inform callbacks. */
9267 if (isSessionMachine())
9268 mParent->onMachineDataChange(mData->mUuid);
9269 }
9270
9271 LogFlowThisFunc(("rc=%08X\n", rc));
9272 LogFlowThisFuncLeave();
9273 return rc;
9274}
9275
9276/**
9277 * Implementation for saving the machine settings into the given
9278 * settings::MachineConfigFile instance. This copies machine extradata
9279 * from the previous machine config file in the instance data, if any.
9280 *
9281 * This gets called from two locations:
9282 *
9283 * -- Machine::saveSettings(), during the regular XML writing;
9284 *
9285 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9286 * exported to OVF and we write the VirtualBox proprietary XML
9287 * into a <vbox:Machine> tag.
9288 *
9289 * This routine fills all the fields in there, including snapshots, *except*
9290 * for the following:
9291 *
9292 * -- fCurrentStateModified. There is some special logic associated with that.
9293 *
9294 * The caller can then call MachineConfigFile::write() or do something else
9295 * with it.
9296 *
9297 * Caller must hold the machine lock!
9298 *
9299 * This throws XML errors and HRESULT, so the caller must have a catch block!
9300 */
9301void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9302{
9303 // deep copy extradata
9304 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9305
9306 config.uuid = mData->mUuid;
9307
9308 // copy name, description, OS type, teleport, UTC etc.
9309 config.machineUserData = mUserData->s;
9310
9311 if ( mData->mMachineState == MachineState_Saved
9312 || mData->mMachineState == MachineState_Restoring
9313 // when deleting a snapshot we may or may not have a saved state in the current state,
9314 // so let's not assert here please
9315 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9316 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9317 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9318 && (!mSSData->strStateFilePath.isEmpty())
9319 )
9320 )
9321 {
9322 Assert(!mSSData->strStateFilePath.isEmpty());
9323 /* try to make the file name relative to the settings file dir */
9324 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9325 }
9326 else
9327 {
9328 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9329 config.strStateFile.setNull();
9330 }
9331
9332 if (mData->mCurrentSnapshot)
9333 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9334 else
9335 config.uuidCurrentSnapshot.clear();
9336
9337 config.timeLastStateChange = mData->mLastStateChange;
9338 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9339 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9340
9341 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9342 if (FAILED(rc)) throw rc;
9343
9344 rc = saveStorageControllers(config.storageMachine);
9345 if (FAILED(rc)) throw rc;
9346
9347 // save machine's media registry if this is VirtualBox 4.0 or later
9348 if (config.canHaveOwnMediaRegistry())
9349 {
9350 // determine machine folder
9351 Utf8Str strMachineFolder = getSettingsFileFull();
9352 strMachineFolder.stripFilename();
9353 mParent->saveMediaRegistry(config.mediaRegistry,
9354 getId(), // only media with registry ID == machine UUID
9355 strMachineFolder);
9356 // this throws HRESULT
9357 }
9358
9359 // save snapshots
9360 rc = saveAllSnapshots(config);
9361 if (FAILED(rc)) throw rc;
9362}
9363
9364/**
9365 * Saves all snapshots of the machine into the given machine config file. Called
9366 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9367 * @param config
9368 * @return
9369 */
9370HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9371{
9372 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9373
9374 HRESULT rc = S_OK;
9375
9376 try
9377 {
9378 config.llFirstSnapshot.clear();
9379
9380 if (mData->mFirstSnapshot)
9381 {
9382 settings::Snapshot snapNew;
9383 config.llFirstSnapshot.push_back(snapNew);
9384
9385 // get reference to the fresh copy of the snapshot on the list and
9386 // work on that copy directly to avoid excessive copying later
9387 settings::Snapshot &snap = config.llFirstSnapshot.front();
9388
9389 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9390 if (FAILED(rc)) throw rc;
9391 }
9392
9393// if (mType == IsSessionMachine)
9394// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9395
9396 }
9397 catch (HRESULT err)
9398 {
9399 /* we assume that error info is set by the thrower */
9400 rc = err;
9401 }
9402 catch (...)
9403 {
9404 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9405 }
9406
9407 return rc;
9408}
9409
9410/**
9411 * Saves the VM hardware configuration. It is assumed that the
9412 * given node is empty.
9413 *
9414 * @param data Reference to the settings object for the hardware config.
9415 * @param pDbg Pointer to the settings object for the debugging config
9416 * which happens to live in mHWData.
9417 * @param pAutostart Pointer to the settings object for the autostart config
9418 * which happens to live in mHWData.
9419 */
9420HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9421 settings::Autostart *pAutostart)
9422{
9423 HRESULT rc = S_OK;
9424
9425 try
9426 {
9427 /* The hardware version attribute (optional).
9428 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9429 if ( mHWData->mHWVersion == "1"
9430 && mSSData->strStateFilePath.isEmpty()
9431 )
9432 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. */
9433
9434 data.strVersion = mHWData->mHWVersion;
9435 data.uuid = mHWData->mHardwareUUID;
9436
9437 // CPU
9438 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9439 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9440 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9441 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9442 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9443 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9444 data.fPAE = !!mHWData->mPAEEnabled;
9445 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9446
9447 /* Standard and Extended CPUID leafs. */
9448 data.llCpuIdLeafs.clear();
9449 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9450 {
9451 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9452 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9453 }
9454 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9455 {
9456 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9457 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9458 }
9459
9460 data.cCPUs = mHWData->mCPUCount;
9461 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9462 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9463
9464 data.llCpus.clear();
9465 if (data.fCpuHotPlug)
9466 {
9467 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9468 {
9469 if (mHWData->mCPUAttached[idx])
9470 {
9471 settings::Cpu cpu;
9472 cpu.ulId = idx;
9473 data.llCpus.push_back(cpu);
9474 }
9475 }
9476 }
9477
9478 // memory
9479 data.ulMemorySizeMB = mHWData->mMemorySize;
9480 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9481
9482 // firmware
9483 data.firmwareType = mHWData->mFirmwareType;
9484
9485 // HID
9486 data.pointingHidType = mHWData->mPointingHidType;
9487 data.keyboardHidType = mHWData->mKeyboardHidType;
9488
9489 // chipset
9490 data.chipsetType = mHWData->mChipsetType;
9491
9492 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9493
9494 // HPET
9495 data.fHpetEnabled = !!mHWData->mHpetEnabled;
9496
9497 // boot order
9498 data.mapBootOrder.clear();
9499 for (size_t i = 0;
9500 i < RT_ELEMENTS(mHWData->mBootOrder);
9501 ++i)
9502 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9503
9504 // display
9505 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9506 data.cMonitors = mHWData->mMonitorCount;
9507 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9508 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9509
9510 /* VRDEServer settings (optional) */
9511 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9512 if (FAILED(rc)) throw rc;
9513
9514 /* BIOS (required) */
9515 rc = mBIOSSettings->saveSettings(data.biosSettings);
9516 if (FAILED(rc)) throw rc;
9517
9518 /* USB Controller (required) */
9519 rc = mUSBController->saveSettings(data.usbController);
9520 if (FAILED(rc)) throw rc;
9521
9522 /* Network adapters (required) */
9523 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9524 data.llNetworkAdapters.clear();
9525 /* Write out only the nominal number of network adapters for this
9526 * chipset type. Since Machine::commit() hasn't been called there
9527 * may be extra NIC settings in the vector. */
9528 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9529 {
9530 settings::NetworkAdapter nic;
9531 nic.ulSlot = slot;
9532 /* paranoia check... must not be NULL, but must not crash either. */
9533 if (mNetworkAdapters[slot])
9534 {
9535 rc = mNetworkAdapters[slot]->saveSettings(nic);
9536 if (FAILED(rc)) throw rc;
9537
9538 data.llNetworkAdapters.push_back(nic);
9539 }
9540 }
9541
9542 /* Serial ports */
9543 data.llSerialPorts.clear();
9544 for (ULONG slot = 0;
9545 slot < RT_ELEMENTS(mSerialPorts);
9546 ++slot)
9547 {
9548 settings::SerialPort s;
9549 s.ulSlot = slot;
9550 rc = mSerialPorts[slot]->saveSettings(s);
9551 if (FAILED(rc)) return rc;
9552
9553 data.llSerialPorts.push_back(s);
9554 }
9555
9556 /* Parallel ports */
9557 data.llParallelPorts.clear();
9558 for (ULONG slot = 0;
9559 slot < RT_ELEMENTS(mParallelPorts);
9560 ++slot)
9561 {
9562 settings::ParallelPort p;
9563 p.ulSlot = slot;
9564 rc = mParallelPorts[slot]->saveSettings(p);
9565 if (FAILED(rc)) return rc;
9566
9567 data.llParallelPorts.push_back(p);
9568 }
9569
9570 /* Audio adapter */
9571 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9572 if (FAILED(rc)) return rc;
9573
9574 /* Shared folders */
9575 data.llSharedFolders.clear();
9576 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9577 it != mHWData->mSharedFolders.end();
9578 ++it)
9579 {
9580 SharedFolder *pSF = *it;
9581 AutoCaller sfCaller(pSF);
9582 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9583 settings::SharedFolder sf;
9584 sf.strName = pSF->getName();
9585 sf.strHostPath = pSF->getHostPath();
9586 sf.fWritable = !!pSF->isWritable();
9587 sf.fAutoMount = !!pSF->isAutoMounted();
9588
9589 data.llSharedFolders.push_back(sf);
9590 }
9591
9592 // clipboard
9593 data.clipboardMode = mHWData->mClipboardMode;
9594
9595 /* Guest */
9596 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9597
9598 // IO settings
9599 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
9600 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
9601
9602 /* BandwidthControl (required) */
9603 rc = mBandwidthControl->saveSettings(data.ioSettings);
9604 if (FAILED(rc)) throw rc;
9605
9606 /* Host PCI devices */
9607 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
9608 it != mHWData->mPciDeviceAssignments.end();
9609 ++it)
9610 {
9611 ComObjPtr<PciDeviceAttachment> pda = *it;
9612 settings::HostPciDeviceAttachment hpda;
9613
9614 rc = pda->saveSettings(hpda);
9615 if (FAILED(rc)) throw rc;
9616
9617 data.pciAttachments.push_back(hpda);
9618 }
9619
9620
9621 // guest properties
9622 data.llGuestProperties.clear();
9623#ifdef VBOX_WITH_GUEST_PROPS
9624 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9625 it != mHWData->mGuestProperties.end();
9626 ++it)
9627 {
9628 HWData::GuestProperty property = *it;
9629
9630 /* Remove transient guest properties at shutdown unless we
9631 * are saving state */
9632 if ( ( mData->mMachineState == MachineState_PoweredOff
9633 || mData->mMachineState == MachineState_Aborted
9634 || mData->mMachineState == MachineState_Teleported)
9635 && ( property.mFlags & guestProp::TRANSIENT
9636 || property.mFlags & guestProp::TRANSRESET))
9637 continue;
9638 settings::GuestProperty prop;
9639 prop.strName = property.strName;
9640 prop.strValue = property.strValue;
9641 prop.timestamp = property.mTimestamp;
9642 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9643 guestProp::writeFlags(property.mFlags, szFlags);
9644 prop.strFlags = szFlags;
9645
9646 data.llGuestProperties.push_back(prop);
9647 }
9648
9649 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9650 /* I presume this doesn't require a backup(). */
9651 mData->mGuestPropertiesModified = FALSE;
9652#endif /* VBOX_WITH_GUEST_PROPS defined */
9653
9654 *pDbg = mHWData->mDebugging;
9655 *pAutostart = mHWData->mAutostart;
9656 }
9657 catch(std::bad_alloc &)
9658 {
9659 return E_OUTOFMEMORY;
9660 }
9661
9662 AssertComRC(rc);
9663 return rc;
9664}
9665
9666/**
9667 * Saves the storage controller configuration.
9668 *
9669 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9670 */
9671HRESULT Machine::saveStorageControllers(settings::Storage &data)
9672{
9673 data.llStorageControllers.clear();
9674
9675 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9676 it != mStorageControllers->end();
9677 ++it)
9678 {
9679 HRESULT rc;
9680 ComObjPtr<StorageController> pCtl = *it;
9681
9682 settings::StorageController ctl;
9683 ctl.strName = pCtl->getName();
9684 ctl.controllerType = pCtl->getControllerType();
9685 ctl.storageBus = pCtl->getStorageBus();
9686 ctl.ulInstance = pCtl->getInstance();
9687 ctl.fBootable = pCtl->getBootable();
9688
9689 /* Save the port count. */
9690 ULONG portCount;
9691 rc = pCtl->COMGETTER(PortCount)(&portCount);
9692 ComAssertComRCRet(rc, rc);
9693 ctl.ulPortCount = portCount;
9694
9695 /* Save fUseHostIOCache */
9696 BOOL fUseHostIOCache;
9697 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9698 ComAssertComRCRet(rc, rc);
9699 ctl.fUseHostIOCache = !!fUseHostIOCache;
9700
9701 /* Save IDE emulation settings. */
9702 if (ctl.controllerType == StorageControllerType_IntelAhci)
9703 {
9704 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9705 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9706 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9707 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9708 )
9709 ComAssertComRCRet(rc, rc);
9710 }
9711
9712 /* save the devices now. */
9713 rc = saveStorageDevices(pCtl, ctl);
9714 ComAssertComRCRet(rc, rc);
9715
9716 data.llStorageControllers.push_back(ctl);
9717 }
9718
9719 return S_OK;
9720}
9721
9722/**
9723 * Saves the hard disk configuration.
9724 */
9725HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9726 settings::StorageController &data)
9727{
9728 MediaData::AttachmentList atts;
9729
9730 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9731 if (FAILED(rc)) return rc;
9732
9733 data.llAttachedDevices.clear();
9734 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9735 it != atts.end();
9736 ++it)
9737 {
9738 settings::AttachedDevice dev;
9739
9740 MediumAttachment *pAttach = *it;
9741 Medium *pMedium = pAttach->getMedium();
9742
9743 dev.deviceType = pAttach->getType();
9744 dev.lPort = pAttach->getPort();
9745 dev.lDevice = pAttach->getDevice();
9746 if (pMedium)
9747 {
9748 if (pMedium->isHostDrive())
9749 dev.strHostDriveSrc = pMedium->getLocationFull();
9750 else
9751 dev.uuid = pMedium->getId();
9752 dev.fPassThrough = pAttach->getPassthrough();
9753 dev.fTempEject = pAttach->getTempEject();
9754 dev.fDiscard = pAttach->getDiscard();
9755 }
9756
9757 dev.strBwGroup = pAttach->getBandwidthGroup();
9758
9759 data.llAttachedDevices.push_back(dev);
9760 }
9761
9762 return S_OK;
9763}
9764
9765/**
9766 * Saves machine state settings as defined by aFlags
9767 * (SaveSTS_* values).
9768 *
9769 * @param aFlags Combination of SaveSTS_* flags.
9770 *
9771 * @note Locks objects for writing.
9772 */
9773HRESULT Machine::saveStateSettings(int aFlags)
9774{
9775 if (aFlags == 0)
9776 return S_OK;
9777
9778 AutoCaller autoCaller(this);
9779 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9780
9781 /* This object's write lock is also necessary to serialize file access
9782 * (prevent concurrent reads and writes) */
9783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9784
9785 HRESULT rc = S_OK;
9786
9787 Assert(mData->pMachineConfigFile);
9788
9789 try
9790 {
9791 if (aFlags & SaveSTS_CurStateModified)
9792 mData->pMachineConfigFile->fCurrentStateModified = true;
9793
9794 if (aFlags & SaveSTS_StateFilePath)
9795 {
9796 if (!mSSData->strStateFilePath.isEmpty())
9797 /* try to make the file name relative to the settings file dir */
9798 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9799 else
9800 mData->pMachineConfigFile->strStateFile.setNull();
9801 }
9802
9803 if (aFlags & SaveSTS_StateTimeStamp)
9804 {
9805 Assert( mData->mMachineState != MachineState_Aborted
9806 || mSSData->strStateFilePath.isEmpty());
9807
9808 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9809
9810 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9811//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9812 }
9813
9814 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9815 }
9816 catch (...)
9817 {
9818 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9819 }
9820
9821 return rc;
9822}
9823
9824/**
9825 * Ensures that the given medium is added to a media registry. If this machine
9826 * was created with 4.0 or later, then the machine registry is used. Otherwise
9827 * the global VirtualBox media registry is used.
9828 *
9829 * Caller must NOT hold machine lock, media tree or any medium locks!
9830 *
9831 * @param pMedium
9832 */
9833void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9834{
9835 /* Paranoia checks: do not hold machine or media tree locks. */
9836 AssertReturnVoid(!isWriteLockOnCurrentThread());
9837 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9838
9839 ComObjPtr<Medium> pBase;
9840 {
9841 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9842 pBase = pMedium->getBase();
9843 }
9844
9845 /* Paranoia checks: do not hold medium locks. */
9846 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9847 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9848
9849 // decide which medium registry to use now that the medium is attached:
9850 Guid uuid;
9851 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9852 // machine XML is VirtualBox 4.0 or higher:
9853 uuid = getId(); // machine UUID
9854 else
9855 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9856
9857 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9858 mParent->markRegistryModified(uuid);
9859
9860 /* For more complex hard disk structures it can happen that the base
9861 * medium isn't yet associated with any medium registry. Do that now. */
9862 if (pMedium != pBase)
9863 {
9864 if (pBase->addRegistry(uuid, true /* fRecurse */))
9865 mParent->markRegistryModified(uuid);
9866 }
9867}
9868
9869/**
9870 * Creates differencing hard disks for all normal hard disks attached to this
9871 * machine and a new set of attachments to refer to created disks.
9872 *
9873 * Used when taking a snapshot or when deleting the current state. Gets called
9874 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9875 *
9876 * This method assumes that mMediaData contains the original hard disk attachments
9877 * it needs to create diffs for. On success, these attachments will be replaced
9878 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9879 * called to delete created diffs which will also rollback mMediaData and restore
9880 * whatever was backed up before calling this method.
9881 *
9882 * Attachments with non-normal hard disks are left as is.
9883 *
9884 * If @a aOnline is @c false then the original hard disks that require implicit
9885 * diffs will be locked for reading. Otherwise it is assumed that they are
9886 * already locked for writing (when the VM was started). Note that in the latter
9887 * case it is responsibility of the caller to lock the newly created diffs for
9888 * writing if this method succeeds.
9889 *
9890 * @param aProgress Progress object to run (must contain at least as
9891 * many operations left as the number of hard disks
9892 * attached).
9893 * @param aOnline Whether the VM was online prior to this operation.
9894 *
9895 * @note The progress object is not marked as completed, neither on success nor
9896 * on failure. This is a responsibility of the caller.
9897 *
9898 * @note Locks this object for writing.
9899 */
9900HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
9901 ULONG aWeight,
9902 bool aOnline)
9903{
9904 LogFlowThisFunc(("aOnline=%d\n", aOnline));
9905
9906 AutoCaller autoCaller(this);
9907 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9908
9909 AutoMultiWriteLock2 alock(this->lockHandle(),
9910 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9911
9912 /* must be in a protective state because we release the lock below */
9913 AssertReturn( mData->mMachineState == MachineState_Saving
9914 || mData->mMachineState == MachineState_LiveSnapshotting
9915 || mData->mMachineState == MachineState_RestoringSnapshot
9916 || mData->mMachineState == MachineState_DeletingSnapshot
9917 , E_FAIL);
9918
9919 HRESULT rc = S_OK;
9920
9921 MediumLockListMap lockedMediaOffline;
9922 MediumLockListMap *lockedMediaMap;
9923 if (aOnline)
9924 lockedMediaMap = &mData->mSession.mLockedMedia;
9925 else
9926 lockedMediaMap = &lockedMediaOffline;
9927
9928 try
9929 {
9930 if (!aOnline)
9931 {
9932 /* lock all attached hard disks early to detect "in use"
9933 * situations before creating actual diffs */
9934 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9935 it != mMediaData->mAttachments.end();
9936 ++it)
9937 {
9938 MediumAttachment* pAtt = *it;
9939 if (pAtt->getType() == DeviceType_HardDisk)
9940 {
9941 Medium* pMedium = pAtt->getMedium();
9942 Assert(pMedium);
9943
9944 MediumLockList *pMediumLockList(new MediumLockList());
9945 alock.release();
9946 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
9947 false /* fMediumLockWrite */,
9948 NULL,
9949 *pMediumLockList);
9950 alock.acquire();
9951 if (FAILED(rc))
9952 {
9953 delete pMediumLockList;
9954 throw rc;
9955 }
9956 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
9957 if (FAILED(rc))
9958 {
9959 throw setError(rc,
9960 tr("Collecting locking information for all attached media failed"));
9961 }
9962 }
9963 }
9964
9965 /* Now lock all media. If this fails, nothing is locked. */
9966 alock.release();
9967 rc = lockedMediaMap->Lock();
9968 alock.acquire();
9969 if (FAILED(rc))
9970 {
9971 throw setError(rc,
9972 tr("Locking of attached media failed"));
9973 }
9974 }
9975
9976 /* remember the current list (note that we don't use backup() since
9977 * mMediaData may be already backed up) */
9978 MediaData::AttachmentList atts = mMediaData->mAttachments;
9979
9980 /* start from scratch */
9981 mMediaData->mAttachments.clear();
9982
9983 /* go through remembered attachments and create diffs for normal hard
9984 * disks and attach them */
9985 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9986 it != atts.end();
9987 ++it)
9988 {
9989 MediumAttachment* pAtt = *it;
9990
9991 DeviceType_T devType = pAtt->getType();
9992 Medium* pMedium = pAtt->getMedium();
9993
9994 if ( devType != DeviceType_HardDisk
9995 || pMedium == NULL
9996 || pMedium->getType() != MediumType_Normal)
9997 {
9998 /* copy the attachment as is */
9999
10000 /** @todo the progress object created in Console::TakeSnaphot
10001 * only expects operations for hard disks. Later other
10002 * device types need to show up in the progress as well. */
10003 if (devType == DeviceType_HardDisk)
10004 {
10005 if (pMedium == NULL)
10006 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10007 aWeight); // weight
10008 else
10009 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10010 pMedium->getBase()->getName().c_str()).raw(),
10011 aWeight); // weight
10012 }
10013
10014 mMediaData->mAttachments.push_back(pAtt);
10015 continue;
10016 }
10017
10018 /* need a diff */
10019 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10020 pMedium->getBase()->getName().c_str()).raw(),
10021 aWeight); // weight
10022
10023 Utf8Str strFullSnapshotFolder;
10024 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10025
10026 ComObjPtr<Medium> diff;
10027 diff.createObject();
10028 // store the diff in the same registry as the parent
10029 // (this cannot fail here because we can't create implicit diffs for
10030 // unregistered images)
10031 Guid uuidRegistryParent;
10032 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10033 Assert(fInRegistry); NOREF(fInRegistry);
10034 rc = diff->init(mParent,
10035 pMedium->getPreferredDiffFormat(),
10036 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10037 uuidRegistryParent);
10038 if (FAILED(rc)) throw rc;
10039
10040 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10041 * the push_back? Looks like we're going to release medium with the
10042 * wrong kind of lock (general issue with if we fail anywhere at all)
10043 * and an orphaned VDI in the snapshots folder. */
10044
10045 /* update the appropriate lock list */
10046 MediumLockList *pMediumLockList;
10047 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10048 AssertComRCThrowRC(rc);
10049 if (aOnline)
10050 {
10051 alock.release();
10052 rc = pMediumLockList->Update(pMedium, false);
10053 alock.acquire();
10054 AssertComRCThrowRC(rc);
10055 }
10056
10057 /* release the locks before the potentially lengthy operation */
10058 alock.release();
10059 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10060 pMediumLockList,
10061 NULL /* aProgress */,
10062 true /* aWait */);
10063 alock.acquire();
10064 if (FAILED(rc)) throw rc;
10065
10066 rc = lockedMediaMap->Unlock();
10067 AssertComRCThrowRC(rc);
10068 alock.release();
10069 rc = pMediumLockList->Append(diff, true);
10070 alock.acquire();
10071 AssertComRCThrowRC(rc);
10072 alock.release();
10073 rc = lockedMediaMap->Lock();
10074 alock.acquire();
10075 AssertComRCThrowRC(rc);
10076
10077 rc = diff->addBackReference(mData->mUuid);
10078 AssertComRCThrowRC(rc);
10079
10080 /* add a new attachment */
10081 ComObjPtr<MediumAttachment> attachment;
10082 attachment.createObject();
10083 rc = attachment->init(this,
10084 diff,
10085 pAtt->getControllerName(),
10086 pAtt->getPort(),
10087 pAtt->getDevice(),
10088 DeviceType_HardDisk,
10089 true /* aImplicit */,
10090 false /* aPassthrough */,
10091 false /* aTempEject */,
10092 pAtt->getNonRotational(),
10093 pAtt->getDiscard(),
10094 pAtt->getBandwidthGroup());
10095 if (FAILED(rc)) throw rc;
10096
10097 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10098 AssertComRCThrowRC(rc);
10099 mMediaData->mAttachments.push_back(attachment);
10100 }
10101 }
10102 catch (HRESULT aRC) { rc = aRC; }
10103
10104 /* unlock all hard disks we locked */
10105 if (!aOnline)
10106 {
10107 ErrorInfoKeeper eik;
10108
10109 HRESULT rc1 = lockedMediaMap->Clear();
10110 AssertComRC(rc1);
10111 }
10112
10113 if (FAILED(rc))
10114 {
10115 MultiResult mrc = rc;
10116
10117 alock.release();
10118 mrc = deleteImplicitDiffs();
10119 }
10120
10121 return rc;
10122}
10123
10124/**
10125 * Deletes implicit differencing hard disks created either by
10126 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10127 *
10128 * Note that to delete hard disks created by #AttachDevice() this method is
10129 * called from #fixupMedia() when the changes are rolled back.
10130 *
10131 * @note Locks this object for writing.
10132 */
10133HRESULT Machine::deleteImplicitDiffs()
10134{
10135 AutoCaller autoCaller(this);
10136 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10137
10138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10139 LogFlowThisFuncEnter();
10140
10141 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10142
10143 HRESULT rc = S_OK;
10144
10145 MediaData::AttachmentList implicitAtts;
10146
10147 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10148
10149 /* enumerate new attachments */
10150 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10151 it != mMediaData->mAttachments.end();
10152 ++it)
10153 {
10154 ComObjPtr<Medium> hd = (*it)->getMedium();
10155 if (hd.isNull())
10156 continue;
10157
10158 if ((*it)->isImplicit())
10159 {
10160 /* deassociate and mark for deletion */
10161 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10162 rc = hd->removeBackReference(mData->mUuid);
10163 AssertComRC(rc);
10164 implicitAtts.push_back(*it);
10165 continue;
10166 }
10167
10168 /* was this hard disk attached before? */
10169 if (!findAttachment(oldAtts, hd))
10170 {
10171 /* no: de-associate */
10172 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10173 rc = hd->removeBackReference(mData->mUuid);
10174 AssertComRC(rc);
10175 continue;
10176 }
10177 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10178 }
10179
10180 /* rollback hard disk changes */
10181 mMediaData.rollback();
10182
10183 MultiResult mrc(S_OK);
10184
10185 /* delete unused implicit diffs */
10186 if (implicitAtts.size() != 0)
10187 {
10188 /* will release the lock before the potentially lengthy
10189 * operation, so protect with the special state (unless already
10190 * protected) */
10191 MachineState_T oldState = mData->mMachineState;
10192 if ( oldState != MachineState_Saving
10193 && oldState != MachineState_LiveSnapshotting
10194 && oldState != MachineState_RestoringSnapshot
10195 && oldState != MachineState_DeletingSnapshot
10196 && oldState != MachineState_DeletingSnapshotOnline
10197 && oldState != MachineState_DeletingSnapshotPaused
10198 )
10199 setMachineState(MachineState_SettingUp);
10200
10201 alock.release();
10202
10203 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10204 it != implicitAtts.end();
10205 ++it)
10206 {
10207 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10208 ComObjPtr<Medium> hd = (*it)->getMedium();
10209
10210 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10211 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10212 mrc = rc;
10213 }
10214
10215 alock.acquire();
10216
10217 if (mData->mMachineState == MachineState_SettingUp)
10218 setMachineState(oldState);
10219 }
10220
10221 return mrc;
10222}
10223
10224/**
10225 * Looks through the given list of media attachments for one with the given parameters
10226 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10227 * can be searched as well if needed.
10228 *
10229 * @param list
10230 * @param aControllerName
10231 * @param aControllerPort
10232 * @param aDevice
10233 * @return
10234 */
10235MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10236 IN_BSTR aControllerName,
10237 LONG aControllerPort,
10238 LONG aDevice)
10239{
10240 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10241 it != ll.end();
10242 ++it)
10243 {
10244 MediumAttachment *pAttach = *it;
10245 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10246 return pAttach;
10247 }
10248
10249 return NULL;
10250}
10251
10252/**
10253 * Looks through the given list of media attachments for one with the given parameters
10254 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10255 * can be searched as well if needed.
10256 *
10257 * @param list
10258 * @param aControllerName
10259 * @param aControllerPort
10260 * @param aDevice
10261 * @return
10262 */
10263MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10264 ComObjPtr<Medium> pMedium)
10265{
10266 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10267 it != ll.end();
10268 ++it)
10269 {
10270 MediumAttachment *pAttach = *it;
10271 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10272 if (pMediumThis == pMedium)
10273 return pAttach;
10274 }
10275
10276 return NULL;
10277}
10278
10279/**
10280 * Looks through the given list of media attachments for one with the given parameters
10281 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10282 * can be searched as well if needed.
10283 *
10284 * @param list
10285 * @param aControllerName
10286 * @param aControllerPort
10287 * @param aDevice
10288 * @return
10289 */
10290MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10291 Guid &id)
10292{
10293 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10294 it != ll.end();
10295 ++it)
10296 {
10297 MediumAttachment *pAttach = *it;
10298 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10299 if (pMediumThis->getId() == id)
10300 return pAttach;
10301 }
10302
10303 return NULL;
10304}
10305
10306/**
10307 * Main implementation for Machine::DetachDevice. This also gets called
10308 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10309 *
10310 * @param pAttach Medium attachment to detach.
10311 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10312 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10313 * @return
10314 */
10315HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10316 AutoWriteLock &writeLock,
10317 Snapshot *pSnapshot)
10318{
10319 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10320 DeviceType_T mediumType = pAttach->getType();
10321
10322 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10323
10324 if (pAttach->isImplicit())
10325 {
10326 /* attempt to implicitly delete the implicitly created diff */
10327
10328 /// @todo move the implicit flag from MediumAttachment to Medium
10329 /// and forbid any hard disk operation when it is implicit. Or maybe
10330 /// a special media state for it to make it even more simple.
10331
10332 Assert(mMediaData.isBackedUp());
10333
10334 /* will release the lock before the potentially lengthy operation, so
10335 * protect with the special state */
10336 MachineState_T oldState = mData->mMachineState;
10337 setMachineState(MachineState_SettingUp);
10338
10339 writeLock.release();
10340
10341 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10342 true /*aWait*/);
10343
10344 writeLock.acquire();
10345
10346 setMachineState(oldState);
10347
10348 if (FAILED(rc)) return rc;
10349 }
10350
10351 setModified(IsModified_Storage);
10352 mMediaData.backup();
10353 mMediaData->mAttachments.remove(pAttach);
10354
10355 if (!oldmedium.isNull())
10356 {
10357 // if this is from a snapshot, do not defer detachment to commitMedia()
10358 if (pSnapshot)
10359 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10360 // else if non-hard disk media, do not defer detachment to commitMedia() either
10361 else if (mediumType != DeviceType_HardDisk)
10362 oldmedium->removeBackReference(mData->mUuid);
10363 }
10364
10365 return S_OK;
10366}
10367
10368/**
10369 * Goes thru all media of the given list and
10370 *
10371 * 1) calls detachDevice() on each of them for this machine and
10372 * 2) adds all Medium objects found in the process to the given list,
10373 * depending on cleanupMode.
10374 *
10375 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10376 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10377 * media to the list.
10378 *
10379 * This gets called from Machine::Unregister, both for the actual Machine and
10380 * the SnapshotMachine objects that might be found in the snapshots.
10381 *
10382 * Requires caller and locking. The machine lock must be passed in because it
10383 * will be passed on to detachDevice which needs it for temporary unlocking.
10384 *
10385 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10386 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10387 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10388 * otherwise no media get added.
10389 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10390 * @return
10391 */
10392HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10393 Snapshot *pSnapshot,
10394 CleanupMode_T cleanupMode,
10395 MediaList &llMedia)
10396{
10397 Assert(isWriteLockOnCurrentThread());
10398
10399 HRESULT rc;
10400
10401 // make a temporary list because detachDevice invalidates iterators into
10402 // mMediaData->mAttachments
10403 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10404
10405 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10406 it != llAttachments2.end();
10407 ++it)
10408 {
10409 ComObjPtr<MediumAttachment> &pAttach = *it;
10410 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10411
10412 if (!pMedium.isNull())
10413 {
10414 AutoCaller mac(pMedium);
10415 if (FAILED(mac.rc())) return mac.rc();
10416 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10417 DeviceType_T devType = pMedium->getDeviceType();
10418 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10419 && devType == DeviceType_HardDisk)
10420 || (cleanupMode == CleanupMode_Full)
10421 )
10422 {
10423 llMedia.push_back(pMedium);
10424 ComObjPtr<Medium> pParent = pMedium->getParent();
10425 /*
10426 * Search for medias which are not attached to any machine, but
10427 * in the chain to an attached disk. Mediums are only consided
10428 * if they are:
10429 * - have only one child
10430 * - no references to any machines
10431 * - are of normal medium type
10432 */
10433 while (!pParent.isNull())
10434 {
10435 AutoCaller mac1(pParent);
10436 if (FAILED(mac1.rc())) return mac1.rc();
10437 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10438 if (pParent->getChildren().size() == 1)
10439 {
10440 if ( pParent->getMachineBackRefCount() == 0
10441 && pParent->getType() == MediumType_Normal
10442 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10443 llMedia.push_back(pParent);
10444 }else
10445 break;
10446 pParent = pParent->getParent();
10447 }
10448 }
10449 }
10450
10451 // real machine: then we need to use the proper method
10452 rc = detachDevice(pAttach, writeLock, pSnapshot);
10453
10454 if (FAILED(rc))
10455 return rc;
10456 }
10457
10458 return S_OK;
10459}
10460
10461/**
10462 * Perform deferred hard disk detachments.
10463 *
10464 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10465 * backed up).
10466 *
10467 * If @a aOnline is @c true then this method will also unlock the old hard disks
10468 * for which the new implicit diffs were created and will lock these new diffs for
10469 * writing.
10470 *
10471 * @param aOnline Whether the VM was online prior to this operation.
10472 *
10473 * @note Locks this object for writing!
10474 */
10475void Machine::commitMedia(bool aOnline /*= false*/)
10476{
10477 AutoCaller autoCaller(this);
10478 AssertComRCReturnVoid(autoCaller.rc());
10479
10480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10481
10482 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10483
10484 HRESULT rc = S_OK;
10485
10486 /* no attach/detach operations -- nothing to do */
10487 if (!mMediaData.isBackedUp())
10488 return;
10489
10490 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10491 bool fMediaNeedsLocking = false;
10492
10493 /* enumerate new attachments */
10494 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10495 it != mMediaData->mAttachments.end();
10496 ++it)
10497 {
10498 MediumAttachment *pAttach = *it;
10499
10500 pAttach->commit();
10501
10502 Medium* pMedium = pAttach->getMedium();
10503 bool fImplicit = pAttach->isImplicit();
10504
10505 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10506 (pMedium) ? pMedium->getName().c_str() : "NULL",
10507 fImplicit));
10508
10509 /** @todo convert all this Machine-based voodoo to MediumAttachment
10510 * based commit logic. */
10511 if (fImplicit)
10512 {
10513 /* convert implicit attachment to normal */
10514 pAttach->setImplicit(false);
10515
10516 if ( aOnline
10517 && pMedium
10518 && pAttach->getType() == DeviceType_HardDisk
10519 )
10520 {
10521 ComObjPtr<Medium> parent = pMedium->getParent();
10522 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10523
10524 /* update the appropriate lock list */
10525 MediumLockList *pMediumLockList;
10526 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10527 AssertComRC(rc);
10528 if (pMediumLockList)
10529 {
10530 /* unlock if there's a need to change the locking */
10531 if (!fMediaNeedsLocking)
10532 {
10533 rc = mData->mSession.mLockedMedia.Unlock();
10534 AssertComRC(rc);
10535 fMediaNeedsLocking = true;
10536 }
10537 rc = pMediumLockList->Update(parent, false);
10538 AssertComRC(rc);
10539 rc = pMediumLockList->Append(pMedium, true);
10540 AssertComRC(rc);
10541 }
10542 }
10543
10544 continue;
10545 }
10546
10547 if (pMedium)
10548 {
10549 /* was this medium attached before? */
10550 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10551 oldIt != oldAtts.end();
10552 ++oldIt)
10553 {
10554 MediumAttachment *pOldAttach = *oldIt;
10555 if (pOldAttach->getMedium() == pMedium)
10556 {
10557 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10558
10559 /* yes: remove from old to avoid de-association */
10560 oldAtts.erase(oldIt);
10561 break;
10562 }
10563 }
10564 }
10565 }
10566
10567 /* enumerate remaining old attachments and de-associate from the
10568 * current machine state */
10569 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10570 it != oldAtts.end();
10571 ++it)
10572 {
10573 MediumAttachment *pAttach = *it;
10574 Medium* pMedium = pAttach->getMedium();
10575
10576 /* Detach only hard disks, since DVD/floppy media is detached
10577 * instantly in MountMedium. */
10578 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10579 {
10580 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10581
10582 /* now de-associate from the current machine state */
10583 rc = pMedium->removeBackReference(mData->mUuid);
10584 AssertComRC(rc);
10585
10586 if (aOnline)
10587 {
10588 /* unlock since medium is not used anymore */
10589 MediumLockList *pMediumLockList;
10590 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10591 AssertComRC(rc);
10592 if (pMediumLockList)
10593 {
10594 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10595 AssertComRC(rc);
10596 }
10597 }
10598 }
10599 }
10600
10601 /* take media locks again so that the locking state is consistent */
10602 if (fMediaNeedsLocking)
10603 {
10604 Assert(aOnline);
10605 rc = mData->mSession.mLockedMedia.Lock();
10606 AssertComRC(rc);
10607 }
10608
10609 /* commit the hard disk changes */
10610 mMediaData.commit();
10611
10612 if (isSessionMachine())
10613 {
10614 /*
10615 * Update the parent machine to point to the new owner.
10616 * This is necessary because the stored parent will point to the
10617 * session machine otherwise and cause crashes or errors later
10618 * when the session machine gets invalid.
10619 */
10620 /** @todo Change the MediumAttachment class to behave like any other
10621 * class in this regard by creating peer MediumAttachment
10622 * objects for session machines and share the data with the peer
10623 * machine.
10624 */
10625 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10626 it != mMediaData->mAttachments.end();
10627 ++it)
10628 {
10629 (*it)->updateParentMachine(mPeer);
10630 }
10631
10632 /* attach new data to the primary machine and reshare it */
10633 mPeer->mMediaData.attach(mMediaData);
10634 }
10635
10636 return;
10637}
10638
10639/**
10640 * Perform deferred deletion of implicitly created diffs.
10641 *
10642 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10643 * backed up).
10644 *
10645 * @note Locks this object for writing!
10646 */
10647void Machine::rollbackMedia()
10648{
10649 AutoCaller autoCaller(this);
10650 AssertComRCReturnVoid (autoCaller.rc());
10651
10652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10653
10654 LogFlowThisFunc(("Entering\n"));
10655
10656 HRESULT rc = S_OK;
10657
10658 /* no attach/detach operations -- nothing to do */
10659 if (!mMediaData.isBackedUp())
10660 return;
10661
10662 /* enumerate new attachments */
10663 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10664 it != mMediaData->mAttachments.end();
10665 ++it)
10666 {
10667 MediumAttachment *pAttach = *it;
10668 /* Fix up the backrefs for DVD/floppy media. */
10669 if (pAttach->getType() != DeviceType_HardDisk)
10670 {
10671 Medium* pMedium = pAttach->getMedium();
10672 if (pMedium)
10673 {
10674 rc = pMedium->removeBackReference(mData->mUuid);
10675 AssertComRC(rc);
10676 }
10677 }
10678
10679 (*it)->rollback();
10680
10681 pAttach = *it;
10682 /* Fix up the backrefs for DVD/floppy media. */
10683 if (pAttach->getType() != DeviceType_HardDisk)
10684 {
10685 Medium* pMedium = pAttach->getMedium();
10686 if (pMedium)
10687 {
10688 rc = pMedium->addBackReference(mData->mUuid);
10689 AssertComRC(rc);
10690 }
10691 }
10692 }
10693
10694 /** @todo convert all this Machine-based voodoo to MediumAttachment
10695 * based rollback logic. */
10696 deleteImplicitDiffs();
10697
10698 return;
10699}
10700
10701/**
10702 * Returns true if the settings file is located in the directory named exactly
10703 * as the machine; this means, among other things, that the machine directory
10704 * should be auto-renamed.
10705 *
10706 * @param aSettingsDir if not NULL, the full machine settings file directory
10707 * name will be assigned there.
10708 *
10709 * @note Doesn't lock anything.
10710 * @note Not thread safe (must be called from this object's lock).
10711 */
10712bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10713{
10714 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10715 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10716 if (aSettingsDir)
10717 *aSettingsDir = strMachineDirName;
10718 strMachineDirName.stripPath(); // vmname
10719 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10720 strConfigFileOnly.stripPath() // vmname.vbox
10721 .stripExt(); // vmname
10722
10723 AssertReturn(!strMachineDirName.isEmpty(), false);
10724 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10725
10726 return strMachineDirName == strConfigFileOnly;
10727}
10728
10729/**
10730 * Discards all changes to machine settings.
10731 *
10732 * @param aNotify Whether to notify the direct session about changes or not.
10733 *
10734 * @note Locks objects for writing!
10735 */
10736void Machine::rollback(bool aNotify)
10737{
10738 AutoCaller autoCaller(this);
10739 AssertComRCReturn(autoCaller.rc(), (void)0);
10740
10741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10742
10743 if (!mStorageControllers.isNull())
10744 {
10745 if (mStorageControllers.isBackedUp())
10746 {
10747 /* unitialize all new devices (absent in the backed up list). */
10748 StorageControllerList::const_iterator it = mStorageControllers->begin();
10749 StorageControllerList *backedList = mStorageControllers.backedUpData();
10750 while (it != mStorageControllers->end())
10751 {
10752 if ( std::find(backedList->begin(), backedList->end(), *it)
10753 == backedList->end()
10754 )
10755 {
10756 (*it)->uninit();
10757 }
10758 ++it;
10759 }
10760
10761 /* restore the list */
10762 mStorageControllers.rollback();
10763 }
10764
10765 /* rollback any changes to devices after restoring the list */
10766 if (mData->flModifications & IsModified_Storage)
10767 {
10768 StorageControllerList::const_iterator it = mStorageControllers->begin();
10769 while (it != mStorageControllers->end())
10770 {
10771 (*it)->rollback();
10772 ++it;
10773 }
10774 }
10775 }
10776
10777 mUserData.rollback();
10778
10779 mHWData.rollback();
10780
10781 if (mData->flModifications & IsModified_Storage)
10782 rollbackMedia();
10783
10784 if (mBIOSSettings)
10785 mBIOSSettings->rollback();
10786
10787 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10788 mVRDEServer->rollback();
10789
10790 if (mAudioAdapter)
10791 mAudioAdapter->rollback();
10792
10793 if (mUSBController && (mData->flModifications & IsModified_USB))
10794 mUSBController->rollback();
10795
10796 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10797 mBandwidthControl->rollback();
10798
10799 if (!mHWData.isNull())
10800 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10801 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10802 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10803 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10804
10805 if (mData->flModifications & IsModified_NetworkAdapters)
10806 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10807 if ( mNetworkAdapters[slot]
10808 && mNetworkAdapters[slot]->isModified())
10809 {
10810 mNetworkAdapters[slot]->rollback();
10811 networkAdapters[slot] = mNetworkAdapters[slot];
10812 }
10813
10814 if (mData->flModifications & IsModified_SerialPorts)
10815 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10816 if ( mSerialPorts[slot]
10817 && mSerialPorts[slot]->isModified())
10818 {
10819 mSerialPorts[slot]->rollback();
10820 serialPorts[slot] = mSerialPorts[slot];
10821 }
10822
10823 if (mData->flModifications & IsModified_ParallelPorts)
10824 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10825 if ( mParallelPorts[slot]
10826 && mParallelPorts[slot]->isModified())
10827 {
10828 mParallelPorts[slot]->rollback();
10829 parallelPorts[slot] = mParallelPorts[slot];
10830 }
10831
10832 if (aNotify)
10833 {
10834 /* inform the direct session about changes */
10835
10836 ComObjPtr<Machine> that = this;
10837 uint32_t flModifications = mData->flModifications;
10838 alock.release();
10839
10840 if (flModifications & IsModified_SharedFolders)
10841 that->onSharedFolderChange();
10842
10843 if (flModifications & IsModified_VRDEServer)
10844 that->onVRDEServerChange(/* aRestart */ TRUE);
10845 if (flModifications & IsModified_USB)
10846 that->onUSBControllerChange();
10847
10848 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10849 if (networkAdapters[slot])
10850 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10851 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10852 if (serialPorts[slot])
10853 that->onSerialPortChange(serialPorts[slot]);
10854 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10855 if (parallelPorts[slot])
10856 that->onParallelPortChange(parallelPorts[slot]);
10857
10858 if (flModifications & IsModified_Storage)
10859 that->onStorageControllerChange();
10860
10861#if 0
10862 if (flModifications & IsModified_BandwidthControl)
10863 that->onBandwidthControlChange();
10864#endif
10865 }
10866}
10867
10868/**
10869 * Commits all the changes to machine settings.
10870 *
10871 * Note that this operation is supposed to never fail.
10872 *
10873 * @note Locks this object and children for writing.
10874 */
10875void Machine::commit()
10876{
10877 AutoCaller autoCaller(this);
10878 AssertComRCReturnVoid(autoCaller.rc());
10879
10880 AutoCaller peerCaller(mPeer);
10881 AssertComRCReturnVoid(peerCaller.rc());
10882
10883 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10884
10885 /*
10886 * use safe commit to ensure Snapshot machines (that share mUserData)
10887 * will still refer to a valid memory location
10888 */
10889 mUserData.commitCopy();
10890
10891 mHWData.commit();
10892
10893 if (mMediaData.isBackedUp())
10894 commitMedia();
10895
10896 mBIOSSettings->commit();
10897 mVRDEServer->commit();
10898 mAudioAdapter->commit();
10899 mUSBController->commit();
10900 mBandwidthControl->commit();
10901
10902 /* Keep the original network adapter count until this point, so that
10903 * discarding a chipset type change will not lose settings. */
10904 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10905 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10906 mNetworkAdapters[slot]->commit();
10907 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10908 mSerialPorts[slot]->commit();
10909 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10910 mParallelPorts[slot]->commit();
10911
10912 bool commitStorageControllers = false;
10913
10914 if (mStorageControllers.isBackedUp())
10915 {
10916 mStorageControllers.commit();
10917
10918 if (mPeer)
10919 {
10920 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
10921
10922 /* Commit all changes to new controllers (this will reshare data with
10923 * peers for those who have peers) */
10924 StorageControllerList *newList = new StorageControllerList();
10925 StorageControllerList::const_iterator it = mStorageControllers->begin();
10926 while (it != mStorageControllers->end())
10927 {
10928 (*it)->commit();
10929
10930 /* look if this controller has a peer device */
10931 ComObjPtr<StorageController> peer = (*it)->getPeer();
10932 if (!peer)
10933 {
10934 /* no peer means the device is a newly created one;
10935 * create a peer owning data this device share it with */
10936 peer.createObject();
10937 peer->init(mPeer, *it, true /* aReshare */);
10938 }
10939 else
10940 {
10941 /* remove peer from the old list */
10942 mPeer->mStorageControllers->remove(peer);
10943 }
10944 /* and add it to the new list */
10945 newList->push_back(peer);
10946
10947 ++it;
10948 }
10949
10950 /* uninit old peer's controllers that are left */
10951 it = mPeer->mStorageControllers->begin();
10952 while (it != mPeer->mStorageControllers->end())
10953 {
10954 (*it)->uninit();
10955 ++it;
10956 }
10957
10958 /* attach new list of controllers to our peer */
10959 mPeer->mStorageControllers.attach(newList);
10960 }
10961 else
10962 {
10963 /* we have no peer (our parent is the newly created machine);
10964 * just commit changes to devices */
10965 commitStorageControllers = true;
10966 }
10967 }
10968 else
10969 {
10970 /* the list of controllers itself is not changed,
10971 * just commit changes to controllers themselves */
10972 commitStorageControllers = true;
10973 }
10974
10975 if (commitStorageControllers)
10976 {
10977 StorageControllerList::const_iterator it = mStorageControllers->begin();
10978 while (it != mStorageControllers->end())
10979 {
10980 (*it)->commit();
10981 ++it;
10982 }
10983 }
10984
10985 if (isSessionMachine())
10986 {
10987 /* attach new data to the primary machine and reshare it */
10988 mPeer->mUserData.attach(mUserData);
10989 mPeer->mHWData.attach(mHWData);
10990 /* mMediaData is reshared by fixupMedia */
10991 // mPeer->mMediaData.attach(mMediaData);
10992 Assert(mPeer->mMediaData.data() == mMediaData.data());
10993 }
10994}
10995
10996/**
10997 * Copies all the hardware data from the given machine.
10998 *
10999 * Currently, only called when the VM is being restored from a snapshot. In
11000 * particular, this implies that the VM is not running during this method's
11001 * call.
11002 *
11003 * @note This method must be called from under this object's lock.
11004 *
11005 * @note This method doesn't call #commit(), so all data remains backed up and
11006 * unsaved.
11007 */
11008void Machine::copyFrom(Machine *aThat)
11009{
11010 AssertReturnVoid(!isSnapshotMachine());
11011 AssertReturnVoid(aThat->isSnapshotMachine());
11012
11013 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11014
11015 mHWData.assignCopy(aThat->mHWData);
11016
11017 // create copies of all shared folders (mHWData after attaching a copy
11018 // contains just references to original objects)
11019 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11020 it != mHWData->mSharedFolders.end();
11021 ++it)
11022 {
11023 ComObjPtr<SharedFolder> folder;
11024 folder.createObject();
11025 HRESULT rc = folder->initCopy(getMachine(), *it);
11026 AssertComRC(rc);
11027 *it = folder;
11028 }
11029
11030 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11031 mVRDEServer->copyFrom(aThat->mVRDEServer);
11032 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11033 mUSBController->copyFrom(aThat->mUSBController);
11034 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11035
11036 /* create private copies of all controllers */
11037 mStorageControllers.backup();
11038 mStorageControllers->clear();
11039 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11040 it != aThat->mStorageControllers->end();
11041 ++it)
11042 {
11043 ComObjPtr<StorageController> ctrl;
11044 ctrl.createObject();
11045 ctrl->initCopy(this, *it);
11046 mStorageControllers->push_back(ctrl);
11047 }
11048
11049 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11050 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11051 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11052 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11053 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11054 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11055}
11056
11057/**
11058 * Returns whether the given storage controller is hotplug capable.
11059 *
11060 * @returns true if the controller supports hotplugging
11061 * false otherwise.
11062 * @param enmCtrlType The controller type to check for.
11063 */
11064bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11065{
11066 switch (enmCtrlType)
11067 {
11068 case StorageControllerType_IntelAhci:
11069 return true;
11070 case StorageControllerType_LsiLogic:
11071 case StorageControllerType_LsiLogicSas:
11072 case StorageControllerType_BusLogic:
11073 case StorageControllerType_PIIX3:
11074 case StorageControllerType_PIIX4:
11075 case StorageControllerType_ICH6:
11076 case StorageControllerType_I82078:
11077 default:
11078 return false;
11079 }
11080}
11081
11082#ifdef VBOX_WITH_RESOURCE_USAGE_API
11083
11084void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11085{
11086 AssertReturnVoid(isWriteLockOnCurrentThread());
11087 AssertPtrReturnVoid(aCollector);
11088
11089 pm::CollectorHAL *hal = aCollector->getHAL();
11090 /* Create sub metrics */
11091 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11092 "Percentage of processor time spent in user mode by the VM process.");
11093 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11094 "Percentage of processor time spent in kernel mode by the VM process.");
11095 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11096 "Size of resident portion of VM process in memory.");
11097 /* Create and register base metrics */
11098 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11099 cpuLoadUser, cpuLoadKernel);
11100 aCollector->registerBaseMetric(cpuLoad);
11101 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11102 ramUsageUsed);
11103 aCollector->registerBaseMetric(ramUsage);
11104
11105 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11106 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11107 new pm::AggregateAvg()));
11108 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11109 new pm::AggregateMin()));
11110 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11111 new pm::AggregateMax()));
11112 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11113 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11114 new pm::AggregateAvg()));
11115 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11116 new pm::AggregateMin()));
11117 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11118 new pm::AggregateMax()));
11119
11120 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11121 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11122 new pm::AggregateAvg()));
11123 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11124 new pm::AggregateMin()));
11125 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11126 new pm::AggregateMax()));
11127
11128
11129 /* Guest metrics collector */
11130 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11131 aCollector->registerGuest(mCollectorGuest);
11132 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11133 this, __PRETTY_FUNCTION__, mCollectorGuest));
11134
11135 /* Create sub metrics */
11136 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11137 "Percentage of processor time spent in user mode as seen by the guest.");
11138 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11139 "Percentage of processor time spent in kernel mode as seen by the guest.");
11140 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11141 "Percentage of processor time spent idling as seen by the guest.");
11142
11143 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11144 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11145 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11146 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11147 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11148 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11149
11150 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11151
11152 /* Create and register base metrics */
11153 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11154 guestLoadUser, guestLoadKernel, guestLoadIdle);
11155 aCollector->registerBaseMetric(guestCpuLoad);
11156
11157 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11158 guestMemTotal, guestMemFree,
11159 guestMemBalloon, guestMemShared,
11160 guestMemCache, guestPagedTotal);
11161 aCollector->registerBaseMetric(guestCpuMem);
11162
11163 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11164 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11165 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11166 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11167
11168 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11169 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11170 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11171 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11172
11173 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11174 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11175 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11176 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11177
11178 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11179 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11180 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11181 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11182
11183 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11184 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11185 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11186 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11187
11188 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11189 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11190 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11191 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11192
11193 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11194 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11195 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11196 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11197
11198 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11199 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11200 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11201 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11202
11203 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11204 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11205 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11206 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11207}
11208
11209void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11210{
11211 AssertReturnVoid(isWriteLockOnCurrentThread());
11212
11213 if (aCollector)
11214 {
11215 aCollector->unregisterMetricsFor(aMachine);
11216 aCollector->unregisterBaseMetricsFor(aMachine);
11217 }
11218}
11219
11220#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11221
11222
11223////////////////////////////////////////////////////////////////////////////////
11224
11225DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11226
11227HRESULT SessionMachine::FinalConstruct()
11228{
11229 LogFlowThisFunc(("\n"));
11230
11231#if defined(RT_OS_WINDOWS)
11232 mIPCSem = NULL;
11233#elif defined(RT_OS_OS2)
11234 mIPCSem = NULLHANDLE;
11235#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11236 mIPCSem = -1;
11237#else
11238# error "Port me!"
11239#endif
11240
11241 return BaseFinalConstruct();
11242}
11243
11244void SessionMachine::FinalRelease()
11245{
11246 LogFlowThisFunc(("\n"));
11247
11248 uninit(Uninit::Unexpected);
11249
11250 BaseFinalRelease();
11251}
11252
11253/**
11254 * @note Must be called only by Machine::openSession() from its own write lock.
11255 */
11256HRESULT SessionMachine::init(Machine *aMachine)
11257{
11258 LogFlowThisFuncEnter();
11259 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11260
11261 AssertReturn(aMachine, E_INVALIDARG);
11262
11263 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11264
11265 /* Enclose the state transition NotReady->InInit->Ready */
11266 AutoInitSpan autoInitSpan(this);
11267 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11268
11269 /* create the interprocess semaphore */
11270#if defined(RT_OS_WINDOWS)
11271 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11272 for (size_t i = 0; i < mIPCSemName.length(); i++)
11273 if (mIPCSemName.raw()[i] == '\\')
11274 mIPCSemName.raw()[i] = '/';
11275 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11276 ComAssertMsgRet(mIPCSem,
11277 ("Cannot create IPC mutex '%ls', err=%d",
11278 mIPCSemName.raw(), ::GetLastError()),
11279 E_FAIL);
11280#elif defined(RT_OS_OS2)
11281 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11282 aMachine->mData->mUuid.raw());
11283 mIPCSemName = ipcSem;
11284 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11285 ComAssertMsgRet(arc == NO_ERROR,
11286 ("Cannot create IPC mutex '%s', arc=%ld",
11287 ipcSem.c_str(), arc),
11288 E_FAIL);
11289#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11290# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11291# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11292 /** @todo Check that this still works correctly. */
11293 AssertCompileSize(key_t, 8);
11294# else
11295 AssertCompileSize(key_t, 4);
11296# endif
11297 key_t key;
11298 mIPCSem = -1;
11299 mIPCKey = "0";
11300 for (uint32_t i = 0; i < 1 << 24; i++)
11301 {
11302 key = ((uint32_t)'V' << 24) | i;
11303 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11304 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11305 {
11306 mIPCSem = sem;
11307 if (sem >= 0)
11308 mIPCKey = BstrFmt("%u", key);
11309 break;
11310 }
11311 }
11312# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11313 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11314 char *pszSemName = NULL;
11315 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11316 key_t key = ::ftok(pszSemName, 'V');
11317 RTStrFree(pszSemName);
11318
11319 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11320# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11321
11322 int errnoSave = errno;
11323 if (mIPCSem < 0 && errnoSave == ENOSYS)
11324 {
11325 setError(E_FAIL,
11326 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11327 "support for SysV IPC. Check the host kernel configuration for "
11328 "CONFIG_SYSVIPC=y"));
11329 return E_FAIL;
11330 }
11331 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11332 * the IPC semaphores */
11333 if (mIPCSem < 0 && errnoSave == ENOSPC)
11334 {
11335#ifdef RT_OS_LINUX
11336 setError(E_FAIL,
11337 tr("Cannot create IPC semaphore because the system limit for the "
11338 "maximum number of semaphore sets (SEMMNI), or the system wide "
11339 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11340 "current set of SysV IPC semaphores can be determined from "
11341 "the file /proc/sysvipc/sem"));
11342#else
11343 setError(E_FAIL,
11344 tr("Cannot create IPC semaphore because the system-imposed limit "
11345 "on the maximum number of allowed semaphores or semaphore "
11346 "identifiers system-wide would be exceeded"));
11347#endif
11348 return E_FAIL;
11349 }
11350 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11351 E_FAIL);
11352 /* set the initial value to 1 */
11353 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11354 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11355 E_FAIL);
11356#else
11357# error "Port me!"
11358#endif
11359
11360 /* memorize the peer Machine */
11361 unconst(mPeer) = aMachine;
11362 /* share the parent pointer */
11363 unconst(mParent) = aMachine->mParent;
11364
11365 /* take the pointers to data to share */
11366 mData.share(aMachine->mData);
11367 mSSData.share(aMachine->mSSData);
11368
11369 mUserData.share(aMachine->mUserData);
11370 mHWData.share(aMachine->mHWData);
11371 mMediaData.share(aMachine->mMediaData);
11372
11373 mStorageControllers.allocate();
11374 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11375 it != aMachine->mStorageControllers->end();
11376 ++it)
11377 {
11378 ComObjPtr<StorageController> ctl;
11379 ctl.createObject();
11380 ctl->init(this, *it);
11381 mStorageControllers->push_back(ctl);
11382 }
11383
11384 unconst(mBIOSSettings).createObject();
11385 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11386 /* create another VRDEServer object that will be mutable */
11387 unconst(mVRDEServer).createObject();
11388 mVRDEServer->init(this, aMachine->mVRDEServer);
11389 /* create another audio adapter object that will be mutable */
11390 unconst(mAudioAdapter).createObject();
11391 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11392 /* create a list of serial ports that will be mutable */
11393 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11394 {
11395 unconst(mSerialPorts[slot]).createObject();
11396 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11397 }
11398 /* create a list of parallel ports that will be mutable */
11399 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11400 {
11401 unconst(mParallelPorts[slot]).createObject();
11402 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11403 }
11404 /* create another USB controller object that will be mutable */
11405 unconst(mUSBController).createObject();
11406 mUSBController->init(this, aMachine->mUSBController);
11407
11408 /* create a list of network adapters that will be mutable */
11409 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11410 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11411 {
11412 unconst(mNetworkAdapters[slot]).createObject();
11413 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11414 }
11415
11416 /* create another bandwidth control object that will be mutable */
11417 unconst(mBandwidthControl).createObject();
11418 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11419
11420 /* default is to delete saved state on Saved -> PoweredOff transition */
11421 mRemoveSavedState = true;
11422
11423 /* Confirm a successful initialization when it's the case */
11424 autoInitSpan.setSucceeded();
11425
11426 LogFlowThisFuncLeave();
11427 return S_OK;
11428}
11429
11430/**
11431 * Uninitializes this session object. If the reason is other than
11432 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11433 *
11434 * @param aReason uninitialization reason
11435 *
11436 * @note Locks mParent + this object for writing.
11437 */
11438void SessionMachine::uninit(Uninit::Reason aReason)
11439{
11440 LogFlowThisFuncEnter();
11441 LogFlowThisFunc(("reason=%d\n", aReason));
11442
11443 /*
11444 * Strongly reference ourselves to prevent this object deletion after
11445 * mData->mSession.mMachine.setNull() below (which can release the last
11446 * reference and call the destructor). Important: this must be done before
11447 * accessing any members (and before AutoUninitSpan that does it as well).
11448 * This self reference will be released as the very last step on return.
11449 */
11450 ComObjPtr<SessionMachine> selfRef = this;
11451
11452 /* Enclose the state transition Ready->InUninit->NotReady */
11453 AutoUninitSpan autoUninitSpan(this);
11454 if (autoUninitSpan.uninitDone())
11455 {
11456 LogFlowThisFunc(("Already uninitialized\n"));
11457 LogFlowThisFuncLeave();
11458 return;
11459 }
11460
11461 if (autoUninitSpan.initFailed())
11462 {
11463 /* We've been called by init() because it's failed. It's not really
11464 * necessary (nor it's safe) to perform the regular uninit sequence
11465 * below, the following is enough.
11466 */
11467 LogFlowThisFunc(("Initialization failed.\n"));
11468#if defined(RT_OS_WINDOWS)
11469 if (mIPCSem)
11470 ::CloseHandle(mIPCSem);
11471 mIPCSem = NULL;
11472#elif defined(RT_OS_OS2)
11473 if (mIPCSem != NULLHANDLE)
11474 ::DosCloseMutexSem(mIPCSem);
11475 mIPCSem = NULLHANDLE;
11476#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11477 if (mIPCSem >= 0)
11478 ::semctl(mIPCSem, 0, IPC_RMID);
11479 mIPCSem = -1;
11480# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11481 mIPCKey = "0";
11482# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11483#else
11484# error "Port me!"
11485#endif
11486 uninitDataAndChildObjects();
11487 mData.free();
11488 unconst(mParent) = NULL;
11489 unconst(mPeer) = NULL;
11490 LogFlowThisFuncLeave();
11491 return;
11492 }
11493
11494 MachineState_T lastState;
11495 {
11496 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11497 lastState = mData->mMachineState;
11498 }
11499 NOREF(lastState);
11500
11501#ifdef VBOX_WITH_USB
11502 // release all captured USB devices, but do this before requesting the locks below
11503 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11504 {
11505 /* Console::captureUSBDevices() is called in the VM process only after
11506 * setting the machine state to Starting or Restoring.
11507 * Console::detachAllUSBDevices() will be called upon successful
11508 * termination. So, we need to release USB devices only if there was
11509 * an abnormal termination of a running VM.
11510 *
11511 * This is identical to SessionMachine::DetachAllUSBDevices except
11512 * for the aAbnormal argument. */
11513 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11514 AssertComRC(rc);
11515 NOREF(rc);
11516
11517 USBProxyService *service = mParent->host()->usbProxyService();
11518 if (service)
11519 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11520 }
11521#endif /* VBOX_WITH_USB */
11522
11523 // we need to lock this object in uninit() because the lock is shared
11524 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11525 // and others need mParent lock, and USB needs host lock.
11526 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11527
11528#if 0
11529 // Trigger async cleanup tasks, avoid doing things here which are not
11530 // vital to be done immediately and maybe need more locks. This calls
11531 // Machine::unregisterMetrics().
11532 mParent->onMachineUninit(mPeer);
11533#else
11534 /*
11535 * It is safe to call Machine::unregisterMetrics() here because
11536 * PerformanceCollector::samplerCallback no longer accesses guest methods
11537 * holding the lock.
11538 */
11539 unregisterMetrics(mParent->performanceCollector(), mPeer);
11540#endif
11541 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11542 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11543 this, __PRETTY_FUNCTION__, mCollectorGuest));
11544 if (mCollectorGuest)
11545 {
11546 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11547 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11548 mCollectorGuest = NULL;
11549 }
11550
11551 if (aReason == Uninit::Abnormal)
11552 {
11553 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11554 Global::IsOnlineOrTransient(lastState)));
11555
11556 /* reset the state to Aborted */
11557 if (mData->mMachineState != MachineState_Aborted)
11558 setMachineState(MachineState_Aborted);
11559 }
11560
11561 // any machine settings modified?
11562 if (mData->flModifications)
11563 {
11564 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11565 rollback(false /* aNotify */);
11566 }
11567
11568 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11569 || !mConsoleTaskData.mSnapshot);
11570 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11571 {
11572 LogWarningThisFunc(("canceling failed save state request!\n"));
11573 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11574 }
11575 else if (!mConsoleTaskData.mSnapshot.isNull())
11576 {
11577 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11578
11579 /* delete all differencing hard disks created (this will also attach
11580 * their parents back by rolling back mMediaData) */
11581 rollbackMedia();
11582
11583 // delete the saved state file (it might have been already created)
11584 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11585 // think it's still in use
11586 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11587 mConsoleTaskData.mSnapshot->uninit();
11588 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11589 }
11590
11591 if (!mData->mSession.mType.isEmpty())
11592 {
11593 /* mType is not null when this machine's process has been started by
11594 * Machine::LaunchVMProcess(), therefore it is our child. We
11595 * need to queue the PID to reap the process (and avoid zombies on
11596 * Linux). */
11597 Assert(mData->mSession.mPid != NIL_RTPROCESS);
11598 mParent->addProcessToReap(mData->mSession.mPid);
11599 }
11600
11601 mData->mSession.mPid = NIL_RTPROCESS;
11602
11603 if (aReason == Uninit::Unexpected)
11604 {
11605 /* Uninitialization didn't come from #checkForDeath(), so tell the
11606 * client watcher thread to update the set of machines that have open
11607 * sessions. */
11608 mParent->updateClientWatcher();
11609 }
11610
11611 /* uninitialize all remote controls */
11612 if (mData->mSession.mRemoteControls.size())
11613 {
11614 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11615 mData->mSession.mRemoteControls.size()));
11616
11617 Data::Session::RemoteControlList::iterator it =
11618 mData->mSession.mRemoteControls.begin();
11619 while (it != mData->mSession.mRemoteControls.end())
11620 {
11621 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11622 HRESULT rc = (*it)->Uninitialize();
11623 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11624 if (FAILED(rc))
11625 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11626 ++it;
11627 }
11628 mData->mSession.mRemoteControls.clear();
11629 }
11630
11631 /*
11632 * An expected uninitialization can come only from #checkForDeath().
11633 * Otherwise it means that something's gone really wrong (for example,
11634 * the Session implementation has released the VirtualBox reference
11635 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11636 * etc). However, it's also possible, that the client releases the IPC
11637 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11638 * but the VirtualBox release event comes first to the server process.
11639 * This case is practically possible, so we should not assert on an
11640 * unexpected uninit, just log a warning.
11641 */
11642
11643 if ((aReason == Uninit::Unexpected))
11644 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11645
11646 if (aReason != Uninit::Normal)
11647 {
11648 mData->mSession.mDirectControl.setNull();
11649 }
11650 else
11651 {
11652 /* this must be null here (see #OnSessionEnd()) */
11653 Assert(mData->mSession.mDirectControl.isNull());
11654 Assert(mData->mSession.mState == SessionState_Unlocking);
11655 Assert(!mData->mSession.mProgress.isNull());
11656 }
11657 if (mData->mSession.mProgress)
11658 {
11659 if (aReason == Uninit::Normal)
11660 mData->mSession.mProgress->notifyComplete(S_OK);
11661 else
11662 mData->mSession.mProgress->notifyComplete(E_FAIL,
11663 COM_IIDOF(ISession),
11664 getComponentName(),
11665 tr("The VM session was aborted"));
11666 mData->mSession.mProgress.setNull();
11667 }
11668
11669 /* remove the association between the peer machine and this session machine */
11670 Assert( (SessionMachine*)mData->mSession.mMachine == this
11671 || aReason == Uninit::Unexpected);
11672
11673 /* reset the rest of session data */
11674 mData->mSession.mMachine.setNull();
11675 mData->mSession.mState = SessionState_Unlocked;
11676 mData->mSession.mType.setNull();
11677
11678 /* close the interprocess semaphore before leaving the exclusive lock */
11679#if defined(RT_OS_WINDOWS)
11680 if (mIPCSem)
11681 ::CloseHandle(mIPCSem);
11682 mIPCSem = NULL;
11683#elif defined(RT_OS_OS2)
11684 if (mIPCSem != NULLHANDLE)
11685 ::DosCloseMutexSem(mIPCSem);
11686 mIPCSem = NULLHANDLE;
11687#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11688 if (mIPCSem >= 0)
11689 ::semctl(mIPCSem, 0, IPC_RMID);
11690 mIPCSem = -1;
11691# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11692 mIPCKey = "0";
11693# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11694#else
11695# error "Port me!"
11696#endif
11697
11698 /* fire an event */
11699 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11700
11701 uninitDataAndChildObjects();
11702
11703 /* free the essential data structure last */
11704 mData.free();
11705
11706 /* release the exclusive lock before setting the below two to NULL */
11707 multilock.release();
11708
11709 unconst(mParent) = NULL;
11710 unconst(mPeer) = NULL;
11711
11712 LogFlowThisFuncLeave();
11713}
11714
11715// util::Lockable interface
11716////////////////////////////////////////////////////////////////////////////////
11717
11718/**
11719 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11720 * with the primary Machine instance (mPeer).
11721 */
11722RWLockHandle *SessionMachine::lockHandle() const
11723{
11724 AssertReturn(mPeer != NULL, NULL);
11725 return mPeer->lockHandle();
11726}
11727
11728// IInternalMachineControl methods
11729////////////////////////////////////////////////////////////////////////////////
11730
11731/**
11732 * Passes collected guest statistics to performance collector object
11733 */
11734STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11735 ULONG aCpuKernel, ULONG aCpuIdle,
11736 ULONG aMemTotal, ULONG aMemFree,
11737 ULONG aMemBalloon, ULONG aMemShared,
11738 ULONG aMemCache, ULONG aPageTotal,
11739 ULONG aAllocVMM, ULONG aFreeVMM,
11740 ULONG aBalloonedVMM, ULONG aSharedVMM)
11741{
11742 if (mCollectorGuest)
11743 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11744 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11745 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11746 aBalloonedVMM, aSharedVMM);
11747
11748 return S_OK;
11749}
11750
11751/**
11752 * @note Locks this object for writing.
11753 */
11754STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11755{
11756 AutoCaller autoCaller(this);
11757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11758
11759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11760
11761 mRemoveSavedState = aRemove;
11762
11763 return S_OK;
11764}
11765
11766/**
11767 * @note Locks the same as #setMachineState() does.
11768 */
11769STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11770{
11771 return setMachineState(aMachineState);
11772}
11773
11774/**
11775 * @note Locks this object for reading.
11776 */
11777STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11778{
11779 AutoCaller autoCaller(this);
11780 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11781
11782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11783
11784#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11785 mIPCSemName.cloneTo(aId);
11786 return S_OK;
11787#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11788# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11789 mIPCKey.cloneTo(aId);
11790# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11791 mData->m_strConfigFileFull.cloneTo(aId);
11792# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11793 return S_OK;
11794#else
11795# error "Port me!"
11796#endif
11797}
11798
11799/**
11800 * @note Locks this object for writing.
11801 */
11802STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11803{
11804 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11805 AutoCaller autoCaller(this);
11806 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11807
11808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11809
11810 if (mData->mSession.mState != SessionState_Locked)
11811 return VBOX_E_INVALID_OBJECT_STATE;
11812
11813 if (!mData->mSession.mProgress.isNull())
11814 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11815
11816 LogFlowThisFunc(("returns S_OK.\n"));
11817 return S_OK;
11818}
11819
11820/**
11821 * @note Locks this object for writing.
11822 */
11823STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11824{
11825 AutoCaller autoCaller(this);
11826 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11827
11828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11829
11830 if (mData->mSession.mState != SessionState_Locked)
11831 return VBOX_E_INVALID_OBJECT_STATE;
11832
11833 /* Finalize the LaunchVMProcess progress object. */
11834 if (mData->mSession.mProgress)
11835 {
11836 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11837 mData->mSession.mProgress.setNull();
11838 }
11839
11840 if (SUCCEEDED((HRESULT)iResult))
11841 {
11842#ifdef VBOX_WITH_RESOURCE_USAGE_API
11843 /* The VM has been powered up successfully, so it makes sense
11844 * now to offer the performance metrics for a running machine
11845 * object. Doing it earlier wouldn't be safe. */
11846 registerMetrics(mParent->performanceCollector(), mPeer,
11847 mData->mSession.mPid);
11848#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11849 }
11850
11851 return S_OK;
11852}
11853
11854/**
11855 * @note Locks this object for writing.
11856 */
11857STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11858{
11859 LogFlowThisFuncEnter();
11860
11861 CheckComArgOutPointerValid(aProgress);
11862
11863 AutoCaller autoCaller(this);
11864 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11865
11866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11867
11868 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11869 E_FAIL);
11870
11871 /* create a progress object to track operation completion */
11872 ComObjPtr<Progress> pProgress;
11873 pProgress.createObject();
11874 pProgress->init(getVirtualBox(),
11875 static_cast<IMachine *>(this) /* aInitiator */,
11876 Bstr(tr("Stopping the virtual machine")).raw(),
11877 FALSE /* aCancelable */);
11878
11879 /* fill in the console task data */
11880 mConsoleTaskData.mLastState = mData->mMachineState;
11881 mConsoleTaskData.mProgress = pProgress;
11882
11883 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11884 setMachineState(MachineState_Stopping);
11885
11886 pProgress.queryInterfaceTo(aProgress);
11887
11888 return S_OK;
11889}
11890
11891/**
11892 * @note Locks this object for writing.
11893 */
11894STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
11895{
11896 LogFlowThisFuncEnter();
11897
11898 AutoCaller autoCaller(this);
11899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11900
11901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11902
11903 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
11904 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
11905 && mConsoleTaskData.mLastState != MachineState_Null,
11906 E_FAIL);
11907
11908 /*
11909 * On failure, set the state to the state we had when BeginPoweringDown()
11910 * was called (this is expected by Console::PowerDown() and the associated
11911 * task). On success the VM process already changed the state to
11912 * MachineState_PoweredOff, so no need to do anything.
11913 */
11914 if (FAILED(iResult))
11915 setMachineState(mConsoleTaskData.mLastState);
11916
11917 /* notify the progress object about operation completion */
11918 Assert(mConsoleTaskData.mProgress);
11919 if (SUCCEEDED(iResult))
11920 mConsoleTaskData.mProgress->notifyComplete(S_OK);
11921 else
11922 {
11923 Utf8Str strErrMsg(aErrMsg);
11924 if (strErrMsg.length())
11925 mConsoleTaskData.mProgress->notifyComplete(iResult,
11926 COM_IIDOF(ISession),
11927 getComponentName(),
11928 strErrMsg.c_str());
11929 else
11930 mConsoleTaskData.mProgress->notifyComplete(iResult);
11931 }
11932
11933 /* clear out the temporary saved state data */
11934 mConsoleTaskData.mLastState = MachineState_Null;
11935 mConsoleTaskData.mProgress.setNull();
11936
11937 LogFlowThisFuncLeave();
11938 return S_OK;
11939}
11940
11941
11942/**
11943 * Goes through the USB filters of the given machine to see if the given
11944 * device matches any filter or not.
11945 *
11946 * @note Locks the same as USBController::hasMatchingFilter() does.
11947 */
11948STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
11949 BOOL *aMatched,
11950 ULONG *aMaskedIfs)
11951{
11952 LogFlowThisFunc(("\n"));
11953
11954 CheckComArgNotNull(aUSBDevice);
11955 CheckComArgOutPointerValid(aMatched);
11956
11957 AutoCaller autoCaller(this);
11958 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11959
11960#ifdef VBOX_WITH_USB
11961 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
11962#else
11963 NOREF(aUSBDevice);
11964 NOREF(aMaskedIfs);
11965 *aMatched = FALSE;
11966#endif
11967
11968 return S_OK;
11969}
11970
11971/**
11972 * @note Locks the same as Host::captureUSBDevice() does.
11973 */
11974STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
11975{
11976 LogFlowThisFunc(("\n"));
11977
11978 AutoCaller autoCaller(this);
11979 AssertComRCReturnRC(autoCaller.rc());
11980
11981#ifdef VBOX_WITH_USB
11982 /* if captureDeviceForVM() fails, it must have set extended error info */
11983 clearError();
11984 MultiResult rc = mParent->host()->checkUSBProxyService();
11985 if (FAILED(rc)) return rc;
11986
11987 USBProxyService *service = mParent->host()->usbProxyService();
11988 AssertReturn(service, E_FAIL);
11989 return service->captureDeviceForVM(this, Guid(aId).ref());
11990#else
11991 NOREF(aId);
11992 return E_NOTIMPL;
11993#endif
11994}
11995
11996/**
11997 * @note Locks the same as Host::detachUSBDevice() does.
11998 */
11999STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12000{
12001 LogFlowThisFunc(("\n"));
12002
12003 AutoCaller autoCaller(this);
12004 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12005
12006#ifdef VBOX_WITH_USB
12007 USBProxyService *service = mParent->host()->usbProxyService();
12008 AssertReturn(service, E_FAIL);
12009 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12010#else
12011 NOREF(aId);
12012 NOREF(aDone);
12013 return E_NOTIMPL;
12014#endif
12015}
12016
12017/**
12018 * Inserts all machine filters to the USB proxy service and then calls
12019 * Host::autoCaptureUSBDevices().
12020 *
12021 * Called by Console from the VM process upon VM startup.
12022 *
12023 * @note Locks what called methods lock.
12024 */
12025STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12026{
12027 LogFlowThisFunc(("\n"));
12028
12029 AutoCaller autoCaller(this);
12030 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12031
12032#ifdef VBOX_WITH_USB
12033 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12034 AssertComRC(rc);
12035 NOREF(rc);
12036
12037 USBProxyService *service = mParent->host()->usbProxyService();
12038 AssertReturn(service, E_FAIL);
12039 return service->autoCaptureDevicesForVM(this);
12040#else
12041 return S_OK;
12042#endif
12043}
12044
12045/**
12046 * Removes all machine filters from the USB proxy service and then calls
12047 * Host::detachAllUSBDevices().
12048 *
12049 * Called by Console from the VM process upon normal VM termination or by
12050 * SessionMachine::uninit() upon abnormal VM termination (from under the
12051 * Machine/SessionMachine lock).
12052 *
12053 * @note Locks what called methods lock.
12054 */
12055STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12056{
12057 LogFlowThisFunc(("\n"));
12058
12059 AutoCaller autoCaller(this);
12060 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12061
12062#ifdef VBOX_WITH_USB
12063 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12064 AssertComRC(rc);
12065 NOREF(rc);
12066
12067 USBProxyService *service = mParent->host()->usbProxyService();
12068 AssertReturn(service, E_FAIL);
12069 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12070#else
12071 NOREF(aDone);
12072 return S_OK;
12073#endif
12074}
12075
12076/**
12077 * @note Locks this object for writing.
12078 */
12079STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12080 IProgress **aProgress)
12081{
12082 LogFlowThisFuncEnter();
12083
12084 AssertReturn(aSession, E_INVALIDARG);
12085 AssertReturn(aProgress, E_INVALIDARG);
12086
12087 AutoCaller autoCaller(this);
12088
12089 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12090 /*
12091 * We don't assert below because it might happen that a non-direct session
12092 * informs us it is closed right after we've been uninitialized -- it's ok.
12093 */
12094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12095
12096 /* get IInternalSessionControl interface */
12097 ComPtr<IInternalSessionControl> control(aSession);
12098
12099 ComAssertRet(!control.isNull(), E_INVALIDARG);
12100
12101 /* Creating a Progress object requires the VirtualBox lock, and
12102 * thus locking it here is required by the lock order rules. */
12103 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12104
12105 if (control == mData->mSession.mDirectControl)
12106 {
12107 ComAssertRet(aProgress, E_POINTER);
12108
12109 /* The direct session is being normally closed by the client process
12110 * ----------------------------------------------------------------- */
12111
12112 /* go to the closing state (essential for all open*Session() calls and
12113 * for #checkForDeath()) */
12114 Assert(mData->mSession.mState == SessionState_Locked);
12115 mData->mSession.mState = SessionState_Unlocking;
12116
12117 /* set direct control to NULL to release the remote instance */
12118 mData->mSession.mDirectControl.setNull();
12119 LogFlowThisFunc(("Direct control is set to NULL\n"));
12120
12121 if (mData->mSession.mProgress)
12122 {
12123 /* finalize the progress, someone might wait if a frontend
12124 * closes the session before powering on the VM. */
12125 mData->mSession.mProgress->notifyComplete(E_FAIL,
12126 COM_IIDOF(ISession),
12127 getComponentName(),
12128 tr("The VM session was closed before any attempt to power it on"));
12129 mData->mSession.mProgress.setNull();
12130 }
12131
12132 /* Create the progress object the client will use to wait until
12133 * #checkForDeath() is called to uninitialize this session object after
12134 * it releases the IPC semaphore.
12135 * Note! Because we're "reusing" mProgress here, this must be a proxy
12136 * object just like for LaunchVMProcess. */
12137 Assert(mData->mSession.mProgress.isNull());
12138 ComObjPtr<ProgressProxy> progress;
12139 progress.createObject();
12140 ComPtr<IUnknown> pPeer(mPeer);
12141 progress->init(mParent, pPeer,
12142 Bstr(tr("Closing session")).raw(),
12143 FALSE /* aCancelable */);
12144 progress.queryInterfaceTo(aProgress);
12145 mData->mSession.mProgress = progress;
12146 }
12147 else
12148 {
12149 /* the remote session is being normally closed */
12150 Data::Session::RemoteControlList::iterator it =
12151 mData->mSession.mRemoteControls.begin();
12152 while (it != mData->mSession.mRemoteControls.end())
12153 {
12154 if (control == *it)
12155 break;
12156 ++it;
12157 }
12158 BOOL found = it != mData->mSession.mRemoteControls.end();
12159 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12160 E_INVALIDARG);
12161 // This MUST be erase(it), not remove(*it) as the latter triggers a
12162 // very nasty use after free due to the place where the value "lives".
12163 mData->mSession.mRemoteControls.erase(it);
12164 }
12165
12166 LogFlowThisFuncLeave();
12167 return S_OK;
12168}
12169
12170/**
12171 * @note Locks this object for writing.
12172 */
12173STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12174{
12175 LogFlowThisFuncEnter();
12176
12177 CheckComArgOutPointerValid(aProgress);
12178 CheckComArgOutPointerValid(aStateFilePath);
12179
12180 AutoCaller autoCaller(this);
12181 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12182
12183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12184
12185 AssertReturn( mData->mMachineState == MachineState_Paused
12186 && mConsoleTaskData.mLastState == MachineState_Null
12187 && mConsoleTaskData.strStateFilePath.isEmpty(),
12188 E_FAIL);
12189
12190 /* create a progress object to track operation completion */
12191 ComObjPtr<Progress> pProgress;
12192 pProgress.createObject();
12193 pProgress->init(getVirtualBox(),
12194 static_cast<IMachine *>(this) /* aInitiator */,
12195 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12196 FALSE /* aCancelable */);
12197
12198 Utf8Str strStateFilePath;
12199 /* stateFilePath is null when the machine is not running */
12200 if (mData->mMachineState == MachineState_Paused)
12201 composeSavedStateFilename(strStateFilePath);
12202
12203 /* fill in the console task data */
12204 mConsoleTaskData.mLastState = mData->mMachineState;
12205 mConsoleTaskData.strStateFilePath = strStateFilePath;
12206 mConsoleTaskData.mProgress = pProgress;
12207
12208 /* set the state to Saving (this is expected by Console::SaveState()) */
12209 setMachineState(MachineState_Saving);
12210
12211 strStateFilePath.cloneTo(aStateFilePath);
12212 pProgress.queryInterfaceTo(aProgress);
12213
12214 return S_OK;
12215}
12216
12217/**
12218 * @note Locks mParent + this object for writing.
12219 */
12220STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12221{
12222 LogFlowThisFunc(("\n"));
12223
12224 AutoCaller autoCaller(this);
12225 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12226
12227 /* endSavingState() need mParent lock */
12228 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12229
12230 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12231 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12232 && mConsoleTaskData.mLastState != MachineState_Null
12233 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12234 E_FAIL);
12235
12236 /*
12237 * On failure, set the state to the state we had when BeginSavingState()
12238 * was called (this is expected by Console::SaveState() and the associated
12239 * task). On success the VM process already changed the state to
12240 * MachineState_Saved, so no need to do anything.
12241 */
12242 if (FAILED(iResult))
12243 setMachineState(mConsoleTaskData.mLastState);
12244
12245 return endSavingState(iResult, aErrMsg);
12246}
12247
12248/**
12249 * @note Locks this object for writing.
12250 */
12251STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12252{
12253 LogFlowThisFunc(("\n"));
12254
12255 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12256
12257 AutoCaller autoCaller(this);
12258 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12259
12260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12261
12262 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12263 || mData->mMachineState == MachineState_Teleported
12264 || mData->mMachineState == MachineState_Aborted
12265 , E_FAIL); /** @todo setError. */
12266
12267 Utf8Str stateFilePathFull = aSavedStateFile;
12268 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12269 if (RT_FAILURE(vrc))
12270 return setError(VBOX_E_FILE_ERROR,
12271 tr("Invalid saved state file path '%ls' (%Rrc)"),
12272 aSavedStateFile,
12273 vrc);
12274
12275 mSSData->strStateFilePath = stateFilePathFull;
12276
12277 /* The below setMachineState() will detect the state transition and will
12278 * update the settings file */
12279
12280 return setMachineState(MachineState_Saved);
12281}
12282
12283STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12284 ComSafeArrayOut(BSTR, aValues),
12285 ComSafeArrayOut(LONG64, aTimestamps),
12286 ComSafeArrayOut(BSTR, aFlags))
12287{
12288 LogFlowThisFunc(("\n"));
12289
12290#ifdef VBOX_WITH_GUEST_PROPS
12291 using namespace guestProp;
12292
12293 AutoCaller autoCaller(this);
12294 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12295
12296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12297
12298 CheckComArgOutSafeArrayPointerValid(aNames);
12299 CheckComArgOutSafeArrayPointerValid(aValues);
12300 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12301 CheckComArgOutSafeArrayPointerValid(aFlags);
12302
12303 size_t cEntries = mHWData->mGuestProperties.size();
12304 com::SafeArray<BSTR> names(cEntries);
12305 com::SafeArray<BSTR> values(cEntries);
12306 com::SafeArray<LONG64> timestamps(cEntries);
12307 com::SafeArray<BSTR> flags(cEntries);
12308 unsigned i = 0;
12309 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12310 it != mHWData->mGuestProperties.end();
12311 ++it)
12312 {
12313 char szFlags[MAX_FLAGS_LEN + 1];
12314 it->strName.cloneTo(&names[i]);
12315 it->strValue.cloneTo(&values[i]);
12316 timestamps[i] = it->mTimestamp;
12317 /* If it is NULL, keep it NULL. */
12318 if (it->mFlags)
12319 {
12320 writeFlags(it->mFlags, szFlags);
12321 Bstr(szFlags).cloneTo(&flags[i]);
12322 }
12323 else
12324 flags[i] = NULL;
12325 ++i;
12326 }
12327 names.detachTo(ComSafeArrayOutArg(aNames));
12328 values.detachTo(ComSafeArrayOutArg(aValues));
12329 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12330 flags.detachTo(ComSafeArrayOutArg(aFlags));
12331 return S_OK;
12332#else
12333 ReturnComNotImplemented();
12334#endif
12335}
12336
12337STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12338 IN_BSTR aValue,
12339 LONG64 aTimestamp,
12340 IN_BSTR aFlags)
12341{
12342 LogFlowThisFunc(("\n"));
12343
12344#ifdef VBOX_WITH_GUEST_PROPS
12345 using namespace guestProp;
12346
12347 CheckComArgStrNotEmptyOrNull(aName);
12348 CheckComArgNotNull(aValue);
12349 CheckComArgNotNull(aFlags);
12350
12351 try
12352 {
12353 /*
12354 * Convert input up front.
12355 */
12356 Utf8Str utf8Name(aName);
12357 uint32_t fFlags = NILFLAG;
12358 if (aFlags)
12359 {
12360 Utf8Str utf8Flags(aFlags);
12361 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12362 AssertRCReturn(vrc, E_INVALIDARG);
12363 }
12364
12365 /*
12366 * Now grab the object lock, validate the state and do the update.
12367 */
12368 AutoCaller autoCaller(this);
12369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12370
12371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12372
12373 switch (mData->mMachineState)
12374 {
12375 case MachineState_Paused:
12376 case MachineState_Running:
12377 case MachineState_Teleporting:
12378 case MachineState_TeleportingPausedVM:
12379 case MachineState_LiveSnapshotting:
12380 case MachineState_DeletingSnapshotOnline:
12381 case MachineState_DeletingSnapshotPaused:
12382 case MachineState_Saving:
12383 break;
12384
12385 default:
12386#ifndef DEBUG_sunlover
12387 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12388 VBOX_E_INVALID_VM_STATE);
12389#else
12390 return VBOX_E_INVALID_VM_STATE;
12391#endif
12392 }
12393
12394 setModified(IsModified_MachineData);
12395 mHWData.backup();
12396
12397 /** @todo r=bird: The careful memory handling doesn't work out here because
12398 * the catch block won't undo any damage we've done. So, if push_back throws
12399 * bad_alloc then you've lost the value.
12400 *
12401 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12402 * since values that changes actually bubbles to the end of the list. Using
12403 * something that has an efficient lookup and can tolerate a bit of updates
12404 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12405 * combination of RTStrCache (for sharing names and getting uniqueness into
12406 * the bargain) and hash/tree is another. */
12407 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12408 iter != mHWData->mGuestProperties.end();
12409 ++iter)
12410 if (utf8Name == iter->strName)
12411 {
12412 mHWData->mGuestProperties.erase(iter);
12413 mData->mGuestPropertiesModified = TRUE;
12414 break;
12415 }
12416 if (aValue != NULL)
12417 {
12418 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12419 mHWData->mGuestProperties.push_back(property);
12420 mData->mGuestPropertiesModified = TRUE;
12421 }
12422
12423 /*
12424 * Send a callback notification if appropriate
12425 */
12426 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12427 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12428 RTSTR_MAX,
12429 utf8Name.c_str(),
12430 RTSTR_MAX, NULL)
12431 )
12432 {
12433 alock.release();
12434
12435 mParent->onGuestPropertyChange(mData->mUuid,
12436 aName,
12437 aValue,
12438 aFlags);
12439 }
12440 }
12441 catch (...)
12442 {
12443 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12444 }
12445 return S_OK;
12446#else
12447 ReturnComNotImplemented();
12448#endif
12449}
12450
12451STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12452 IMediumAttachment **aNewAttachment)
12453{
12454 CheckComArgNotNull(aAttachment);
12455 CheckComArgOutPointerValid(aNewAttachment);
12456
12457 AutoCaller autoCaller(this);
12458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12459
12460 // request the host lock first, since might be calling Host methods for getting host drives;
12461 // next, protect the media tree all the while we're in here, as well as our member variables
12462 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12463 this->lockHandle(),
12464 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12465
12466 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12467
12468 Bstr ctrlName;
12469 LONG lPort;
12470 LONG lDevice;
12471 bool fTempEject;
12472 {
12473 AutoCaller autoAttachCaller(this);
12474 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12475
12476 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12477
12478 /* Need to query the details first, as the IMediumAttachment reference
12479 * might be to the original settings, which we are going to change. */
12480 ctrlName = pAttach->getControllerName();
12481 lPort = pAttach->getPort();
12482 lDevice = pAttach->getDevice();
12483 fTempEject = pAttach->getTempEject();
12484 }
12485
12486 if (!fTempEject)
12487 {
12488 /* Remember previously mounted medium. The medium before taking the
12489 * backup is not necessarily the same thing. */
12490 ComObjPtr<Medium> oldmedium;
12491 oldmedium = pAttach->getMedium();
12492
12493 setModified(IsModified_Storage);
12494 mMediaData.backup();
12495
12496 // The backup operation makes the pAttach reference point to the
12497 // old settings. Re-get the correct reference.
12498 pAttach = findAttachment(mMediaData->mAttachments,
12499 ctrlName.raw(),
12500 lPort,
12501 lDevice);
12502
12503 {
12504 AutoCaller autoAttachCaller(this);
12505 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12506
12507 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12508 if (!oldmedium.isNull())
12509 oldmedium->removeBackReference(mData->mUuid);
12510
12511 pAttach->updateMedium(NULL);
12512 pAttach->updateEjected();
12513 }
12514
12515 setModified(IsModified_Storage);
12516 }
12517 else
12518 {
12519 {
12520 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12521 pAttach->updateEjected();
12522 }
12523 }
12524
12525 pAttach.queryInterfaceTo(aNewAttachment);
12526
12527 return S_OK;
12528}
12529
12530// public methods only for internal purposes
12531/////////////////////////////////////////////////////////////////////////////
12532
12533/**
12534 * Called from the client watcher thread to check for expected or unexpected
12535 * death of the client process that has a direct session to this machine.
12536 *
12537 * On Win32 and on OS/2, this method is called only when we've got the
12538 * mutex (i.e. the client has either died or terminated normally) so it always
12539 * returns @c true (the client is terminated, the session machine is
12540 * uninitialized).
12541 *
12542 * On other platforms, the method returns @c true if the client process has
12543 * terminated normally or abnormally and the session machine was uninitialized,
12544 * and @c false if the client process is still alive.
12545 *
12546 * @note Locks this object for writing.
12547 */
12548bool SessionMachine::checkForDeath()
12549{
12550 Uninit::Reason reason;
12551 bool terminated = false;
12552
12553 /* Enclose autoCaller with a block because calling uninit() from under it
12554 * will deadlock. */
12555 {
12556 AutoCaller autoCaller(this);
12557 if (!autoCaller.isOk())
12558 {
12559 /* return true if not ready, to cause the client watcher to exclude
12560 * the corresponding session from watching */
12561 LogFlowThisFunc(("Already uninitialized!\n"));
12562 return true;
12563 }
12564
12565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12566
12567 /* Determine the reason of death: if the session state is Closing here,
12568 * everything is fine. Otherwise it means that the client did not call
12569 * OnSessionEnd() before it released the IPC semaphore. This may happen
12570 * either because the client process has abnormally terminated, or
12571 * because it simply forgot to call ISession::Close() before exiting. We
12572 * threat the latter also as an abnormal termination (see
12573 * Session::uninit() for details). */
12574 reason = mData->mSession.mState == SessionState_Unlocking ?
12575 Uninit::Normal :
12576 Uninit::Abnormal;
12577
12578#if defined(RT_OS_WINDOWS)
12579
12580 AssertMsg(mIPCSem, ("semaphore must be created"));
12581
12582 /* release the IPC mutex */
12583 ::ReleaseMutex(mIPCSem);
12584
12585 terminated = true;
12586
12587#elif defined(RT_OS_OS2)
12588
12589 AssertMsg(mIPCSem, ("semaphore must be created"));
12590
12591 /* release the IPC mutex */
12592 ::DosReleaseMutexSem(mIPCSem);
12593
12594 terminated = true;
12595
12596#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12597
12598 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12599
12600 int val = ::semctl(mIPCSem, 0, GETVAL);
12601 if (val > 0)
12602 {
12603 /* the semaphore is signaled, meaning the session is terminated */
12604 terminated = true;
12605 }
12606
12607#else
12608# error "Port me!"
12609#endif
12610
12611 } /* AutoCaller block */
12612
12613 if (terminated)
12614 uninit(reason);
12615
12616 return terminated;
12617}
12618
12619/**
12620 * @note Locks this object for reading.
12621 */
12622HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12623{
12624 LogFlowThisFunc(("\n"));
12625
12626 AutoCaller autoCaller(this);
12627 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12628
12629 ComPtr<IInternalSessionControl> directControl;
12630 {
12631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12632 directControl = mData->mSession.mDirectControl;
12633 }
12634
12635 /* ignore notifications sent after #OnSessionEnd() is called */
12636 if (!directControl)
12637 return S_OK;
12638
12639 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12640}
12641
12642/**
12643 * @note Locks this object for reading.
12644 */
12645HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12646 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12647{
12648 LogFlowThisFunc(("\n"));
12649
12650 AutoCaller autoCaller(this);
12651 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12652
12653 ComPtr<IInternalSessionControl> directControl;
12654 {
12655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12656 directControl = mData->mSession.mDirectControl;
12657 }
12658
12659 /* ignore notifications sent after #OnSessionEnd() is called */
12660 if (!directControl)
12661 return S_OK;
12662 /*
12663 * instead acting like callback we ask IVirtualBox deliver corresponding event
12664 */
12665
12666 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12667 return S_OK;
12668}
12669
12670/**
12671 * @note Locks this object for reading.
12672 */
12673HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12674{
12675 LogFlowThisFunc(("\n"));
12676
12677 AutoCaller autoCaller(this);
12678 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12679
12680 ComPtr<IInternalSessionControl> directControl;
12681 {
12682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12683 directControl = mData->mSession.mDirectControl;
12684 }
12685
12686 /* ignore notifications sent after #OnSessionEnd() is called */
12687 if (!directControl)
12688 return S_OK;
12689
12690 return directControl->OnSerialPortChange(serialPort);
12691}
12692
12693/**
12694 * @note Locks this object for reading.
12695 */
12696HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12697{
12698 LogFlowThisFunc(("\n"));
12699
12700 AutoCaller autoCaller(this);
12701 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12702
12703 ComPtr<IInternalSessionControl> directControl;
12704 {
12705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12706 directControl = mData->mSession.mDirectControl;
12707 }
12708
12709 /* ignore notifications sent after #OnSessionEnd() is called */
12710 if (!directControl)
12711 return S_OK;
12712
12713 return directControl->OnParallelPortChange(parallelPort);
12714}
12715
12716/**
12717 * @note Locks this object for reading.
12718 */
12719HRESULT SessionMachine::onStorageControllerChange()
12720{
12721 LogFlowThisFunc(("\n"));
12722
12723 AutoCaller autoCaller(this);
12724 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12725
12726 ComPtr<IInternalSessionControl> directControl;
12727 {
12728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12729 directControl = mData->mSession.mDirectControl;
12730 }
12731
12732 /* ignore notifications sent after #OnSessionEnd() is called */
12733 if (!directControl)
12734 return S_OK;
12735
12736 return directControl->OnStorageControllerChange();
12737}
12738
12739/**
12740 * @note Locks this object for reading.
12741 */
12742HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12743{
12744 LogFlowThisFunc(("\n"));
12745
12746 AutoCaller autoCaller(this);
12747 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12748
12749 ComPtr<IInternalSessionControl> directControl;
12750 {
12751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12752 directControl = mData->mSession.mDirectControl;
12753 }
12754
12755 /* ignore notifications sent after #OnSessionEnd() is called */
12756 if (!directControl)
12757 return S_OK;
12758
12759 return directControl->OnMediumChange(aAttachment, aForce);
12760}
12761
12762/**
12763 * @note Locks this object for reading.
12764 */
12765HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12766{
12767 LogFlowThisFunc(("\n"));
12768
12769 AutoCaller autoCaller(this);
12770 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12771
12772 ComPtr<IInternalSessionControl> directControl;
12773 {
12774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12775 directControl = mData->mSession.mDirectControl;
12776 }
12777
12778 /* ignore notifications sent after #OnSessionEnd() is called */
12779 if (!directControl)
12780 return S_OK;
12781
12782 return directControl->OnCPUChange(aCPU, aRemove);
12783}
12784
12785HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12786{
12787 LogFlowThisFunc(("\n"));
12788
12789 AutoCaller autoCaller(this);
12790 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12791
12792 ComPtr<IInternalSessionControl> directControl;
12793 {
12794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12795 directControl = mData->mSession.mDirectControl;
12796 }
12797
12798 /* ignore notifications sent after #OnSessionEnd() is called */
12799 if (!directControl)
12800 return S_OK;
12801
12802 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12803}
12804
12805/**
12806 * @note Locks this object for reading.
12807 */
12808HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12809{
12810 LogFlowThisFunc(("\n"));
12811
12812 AutoCaller autoCaller(this);
12813 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12814
12815 ComPtr<IInternalSessionControl> directControl;
12816 {
12817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12818 directControl = mData->mSession.mDirectControl;
12819 }
12820
12821 /* ignore notifications sent after #OnSessionEnd() is called */
12822 if (!directControl)
12823 return S_OK;
12824
12825 return directControl->OnVRDEServerChange(aRestart);
12826}
12827
12828/**
12829 * @note Locks this object for reading.
12830 */
12831HRESULT SessionMachine::onUSBControllerChange()
12832{
12833 LogFlowThisFunc(("\n"));
12834
12835 AutoCaller autoCaller(this);
12836 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12837
12838 ComPtr<IInternalSessionControl> directControl;
12839 {
12840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12841 directControl = mData->mSession.mDirectControl;
12842 }
12843
12844 /* ignore notifications sent after #OnSessionEnd() is called */
12845 if (!directControl)
12846 return S_OK;
12847
12848 return directControl->OnUSBControllerChange();
12849}
12850
12851/**
12852 * @note Locks this object for reading.
12853 */
12854HRESULT SessionMachine::onSharedFolderChange()
12855{
12856 LogFlowThisFunc(("\n"));
12857
12858 AutoCaller autoCaller(this);
12859 AssertComRCReturnRC(autoCaller.rc());
12860
12861 ComPtr<IInternalSessionControl> directControl;
12862 {
12863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12864 directControl = mData->mSession.mDirectControl;
12865 }
12866
12867 /* ignore notifications sent after #OnSessionEnd() is called */
12868 if (!directControl)
12869 return S_OK;
12870
12871 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12872}
12873
12874/**
12875 * @note Locks this object for reading.
12876 */
12877HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12878{
12879 LogFlowThisFunc(("\n"));
12880
12881 AutoCaller autoCaller(this);
12882 AssertComRCReturnRC(autoCaller.rc());
12883
12884 ComPtr<IInternalSessionControl> directControl;
12885 {
12886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12887 directControl = mData->mSession.mDirectControl;
12888 }
12889
12890 /* ignore notifications sent after #OnSessionEnd() is called */
12891 if (!directControl)
12892 return S_OK;
12893
12894 return directControl->OnClipboardModeChange(aClipboardMode);
12895}
12896
12897/**
12898 * @note Locks this object for reading.
12899 */
12900HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
12901{
12902 LogFlowThisFunc(("\n"));
12903
12904 AutoCaller autoCaller(this);
12905 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12906
12907 ComPtr<IInternalSessionControl> directControl;
12908 {
12909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12910 directControl = mData->mSession.mDirectControl;
12911 }
12912
12913 /* ignore notifications sent after #OnSessionEnd() is called */
12914 if (!directControl)
12915 return S_OK;
12916
12917 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
12918}
12919
12920/**
12921 * @note Locks this object for reading.
12922 */
12923HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
12924{
12925 LogFlowThisFunc(("\n"));
12926
12927 AutoCaller autoCaller(this);
12928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12929
12930 ComPtr<IInternalSessionControl> directControl;
12931 {
12932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12933 directControl = mData->mSession.mDirectControl;
12934 }
12935
12936 /* ignore notifications sent after #OnSessionEnd() is called */
12937 if (!directControl)
12938 return S_OK;
12939
12940 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
12941}
12942
12943/**
12944 * Returns @c true if this machine's USB controller reports it has a matching
12945 * filter for the given USB device and @c false otherwise.
12946 *
12947 * @note locks this object for reading.
12948 */
12949bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
12950{
12951 AutoCaller autoCaller(this);
12952 /* silently return if not ready -- this method may be called after the
12953 * direct machine session has been called */
12954 if (!autoCaller.isOk())
12955 return false;
12956
12957#ifdef VBOX_WITH_USB
12958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12959
12960 switch (mData->mMachineState)
12961 {
12962 case MachineState_Starting:
12963 case MachineState_Restoring:
12964 case MachineState_TeleportingIn:
12965 case MachineState_Paused:
12966 case MachineState_Running:
12967 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
12968 * elsewhere... */
12969 alock.release();
12970 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
12971 default: break;
12972 }
12973#else
12974 NOREF(aDevice);
12975 NOREF(aMaskedIfs);
12976#endif
12977 return false;
12978}
12979
12980/**
12981 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12982 */
12983HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
12984 IVirtualBoxErrorInfo *aError,
12985 ULONG aMaskedIfs)
12986{
12987 LogFlowThisFunc(("\n"));
12988
12989 AutoCaller autoCaller(this);
12990
12991 /* This notification may happen after the machine object has been
12992 * uninitialized (the session was closed), so don't assert. */
12993 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12994
12995 ComPtr<IInternalSessionControl> directControl;
12996 {
12997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12998 directControl = mData->mSession.mDirectControl;
12999 }
13000
13001 /* fail on notifications sent after #OnSessionEnd() is called, it is
13002 * expected by the caller */
13003 if (!directControl)
13004 return E_FAIL;
13005
13006 /* No locks should be held at this point. */
13007 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13008 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13009
13010 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13011}
13012
13013/**
13014 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13015 */
13016HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13017 IVirtualBoxErrorInfo *aError)
13018{
13019 LogFlowThisFunc(("\n"));
13020
13021 AutoCaller autoCaller(this);
13022
13023 /* This notification may happen after the machine object has been
13024 * uninitialized (the session was closed), so don't assert. */
13025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13026
13027 ComPtr<IInternalSessionControl> directControl;
13028 {
13029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13030 directControl = mData->mSession.mDirectControl;
13031 }
13032
13033 /* fail on notifications sent after #OnSessionEnd() is called, it is
13034 * expected by the caller */
13035 if (!directControl)
13036 return E_FAIL;
13037
13038 /* No locks should be held at this point. */
13039 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13040 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13041
13042 return directControl->OnUSBDeviceDetach(aId, aError);
13043}
13044
13045// protected methods
13046/////////////////////////////////////////////////////////////////////////////
13047
13048/**
13049 * Helper method to finalize saving the state.
13050 *
13051 * @note Must be called from under this object's lock.
13052 *
13053 * @param aRc S_OK if the snapshot has been taken successfully
13054 * @param aErrMsg human readable error message for failure
13055 *
13056 * @note Locks mParent + this objects for writing.
13057 */
13058HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13059{
13060 LogFlowThisFuncEnter();
13061
13062 AutoCaller autoCaller(this);
13063 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13064
13065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13066
13067 HRESULT rc = S_OK;
13068
13069 if (SUCCEEDED(aRc))
13070 {
13071 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13072
13073 /* save all VM settings */
13074 rc = saveSettings(NULL);
13075 // no need to check whether VirtualBox.xml needs saving also since
13076 // we can't have a name change pending at this point
13077 }
13078 else
13079 {
13080 // delete the saved state file (it might have been already created);
13081 // we need not check whether this is shared with a snapshot here because
13082 // we certainly created this saved state file here anew
13083 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13084 }
13085
13086 /* notify the progress object about operation completion */
13087 Assert(mConsoleTaskData.mProgress);
13088 if (SUCCEEDED(aRc))
13089 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13090 else
13091 {
13092 if (aErrMsg.length())
13093 mConsoleTaskData.mProgress->notifyComplete(aRc,
13094 COM_IIDOF(ISession),
13095 getComponentName(),
13096 aErrMsg.c_str());
13097 else
13098 mConsoleTaskData.mProgress->notifyComplete(aRc);
13099 }
13100
13101 /* clear out the temporary saved state data */
13102 mConsoleTaskData.mLastState = MachineState_Null;
13103 mConsoleTaskData.strStateFilePath.setNull();
13104 mConsoleTaskData.mProgress.setNull();
13105
13106 LogFlowThisFuncLeave();
13107 return rc;
13108}
13109
13110/**
13111 * Deletes the given file if it is no longer in use by either the current machine state
13112 * (if the machine is "saved") or any of the machine's snapshots.
13113 *
13114 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13115 * but is different for each SnapshotMachine. When calling this, the order of calling this
13116 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13117 * is therefore critical. I know, it's all rather messy.
13118 *
13119 * @param strStateFile
13120 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13121 */
13122void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13123 Snapshot *pSnapshotToIgnore)
13124{
13125 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13126 if ( (strStateFile.isNotEmpty())
13127 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13128 )
13129 // ... and it must also not be shared with other snapshots
13130 if ( !mData->mFirstSnapshot
13131 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13132 // this checks the SnapshotMachine's state file paths
13133 )
13134 RTFileDelete(strStateFile.c_str());
13135}
13136
13137/**
13138 * Locks the attached media.
13139 *
13140 * All attached hard disks are locked for writing and DVD/floppy are locked for
13141 * reading. Parents of attached hard disks (if any) are locked for reading.
13142 *
13143 * This method also performs accessibility check of all media it locks: if some
13144 * media is inaccessible, the method will return a failure and a bunch of
13145 * extended error info objects per each inaccessible medium.
13146 *
13147 * Note that this method is atomic: if it returns a success, all media are
13148 * locked as described above; on failure no media is locked at all (all
13149 * succeeded individual locks will be undone).
13150 *
13151 * This method is intended to be called when the machine is in Starting or
13152 * Restoring state and asserts otherwise.
13153 *
13154 * The locks made by this method must be undone by calling #unlockMedia() when
13155 * no more needed.
13156 */
13157HRESULT SessionMachine::lockMedia()
13158{
13159 AutoCaller autoCaller(this);
13160 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13161
13162 AutoMultiWriteLock2 alock(this->lockHandle(),
13163 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13164
13165 AssertReturn( mData->mMachineState == MachineState_Starting
13166 || mData->mMachineState == MachineState_Restoring
13167 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13168 /* bail out if trying to lock things with already set up locking */
13169 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13170
13171 clearError();
13172 MultiResult mrc(S_OK);
13173
13174 /* Collect locking information for all medium objects attached to the VM. */
13175 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13176 it != mMediaData->mAttachments.end();
13177 ++it)
13178 {
13179 MediumAttachment* pAtt = *it;
13180 DeviceType_T devType = pAtt->getType();
13181 Medium *pMedium = pAtt->getMedium();
13182
13183 MediumLockList *pMediumLockList(new MediumLockList());
13184 // There can be attachments without a medium (floppy/dvd), and thus
13185 // it's impossible to create a medium lock list. It still makes sense
13186 // to have the empty medium lock list in the map in case a medium is
13187 // attached later.
13188 if (pMedium != NULL)
13189 {
13190 MediumType_T mediumType = pMedium->getType();
13191 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13192 || mediumType == MediumType_Shareable;
13193 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13194
13195 alock.release();
13196 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13197 !fIsReadOnlyLock /* fMediumLockWrite */,
13198 NULL,
13199 *pMediumLockList);
13200 alock.acquire();
13201 if (FAILED(mrc))
13202 {
13203 delete pMediumLockList;
13204 mData->mSession.mLockedMedia.Clear();
13205 break;
13206 }
13207 }
13208
13209 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13210 if (FAILED(rc))
13211 {
13212 mData->mSession.mLockedMedia.Clear();
13213 mrc = setError(rc,
13214 tr("Collecting locking information for all attached media failed"));
13215 break;
13216 }
13217 }
13218
13219 if (SUCCEEDED(mrc))
13220 {
13221 /* Now lock all media. If this fails, nothing is locked. */
13222 alock.release();
13223 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13224 alock.acquire();
13225 if (FAILED(rc))
13226 {
13227 mrc = setError(rc,
13228 tr("Locking of attached media failed"));
13229 }
13230 }
13231
13232 return mrc;
13233}
13234
13235/**
13236 * Undoes the locks made by by #lockMedia().
13237 */
13238void SessionMachine::unlockMedia()
13239{
13240 AutoCaller autoCaller(this);
13241 AssertComRCReturnVoid(autoCaller.rc());
13242
13243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13244
13245 /* we may be holding important error info on the current thread;
13246 * preserve it */
13247 ErrorInfoKeeper eik;
13248
13249 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13250 AssertComRC(rc);
13251}
13252
13253/**
13254 * Helper to change the machine state (reimplementation).
13255 *
13256 * @note Locks this object for writing.
13257 * @note This method must not call saveSettings or SaveSettings, otherwise
13258 * it can cause crashes in random places due to unexpectedly committing
13259 * the current settings. The caller is responsible for that. The call
13260 * to saveStateSettings is fine, because this method does not commit.
13261 */
13262HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13263{
13264 LogFlowThisFuncEnter();
13265 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13266
13267 AutoCaller autoCaller(this);
13268 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13269
13270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13271
13272 MachineState_T oldMachineState = mData->mMachineState;
13273
13274 AssertMsgReturn(oldMachineState != aMachineState,
13275 ("oldMachineState=%s, aMachineState=%s\n",
13276 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13277 E_FAIL);
13278
13279 HRESULT rc = S_OK;
13280
13281 int stsFlags = 0;
13282 bool deleteSavedState = false;
13283
13284 /* detect some state transitions */
13285
13286 if ( ( oldMachineState == MachineState_Saved
13287 && aMachineState == MachineState_Restoring)
13288 || ( ( oldMachineState == MachineState_PoweredOff
13289 || oldMachineState == MachineState_Teleported
13290 || oldMachineState == MachineState_Aborted
13291 )
13292 && ( aMachineState == MachineState_TeleportingIn
13293 || aMachineState == MachineState_Starting
13294 )
13295 )
13296 )
13297 {
13298 /* The EMT thread is about to start */
13299
13300 /* Nothing to do here for now... */
13301
13302 /// @todo NEWMEDIA don't let mDVDDrive and other children
13303 /// change anything when in the Starting/Restoring state
13304 }
13305 else if ( ( oldMachineState == MachineState_Running
13306 || oldMachineState == MachineState_Paused
13307 || oldMachineState == MachineState_Teleporting
13308 || oldMachineState == MachineState_LiveSnapshotting
13309 || oldMachineState == MachineState_Stuck
13310 || oldMachineState == MachineState_Starting
13311 || oldMachineState == MachineState_Stopping
13312 || oldMachineState == MachineState_Saving
13313 || oldMachineState == MachineState_Restoring
13314 || oldMachineState == MachineState_TeleportingPausedVM
13315 || oldMachineState == MachineState_TeleportingIn
13316 )
13317 && ( aMachineState == MachineState_PoweredOff
13318 || aMachineState == MachineState_Saved
13319 || aMachineState == MachineState_Teleported
13320 || aMachineState == MachineState_Aborted
13321 )
13322 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13323 * snapshot */
13324 && ( mConsoleTaskData.mSnapshot.isNull()
13325 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13326 )
13327 )
13328 {
13329 /* The EMT thread has just stopped, unlock attached media. Note that as
13330 * opposed to locking that is done from Console, we do unlocking here
13331 * because the VM process may have aborted before having a chance to
13332 * properly unlock all media it locked. */
13333
13334 unlockMedia();
13335 }
13336
13337 if (oldMachineState == MachineState_Restoring)
13338 {
13339 if (aMachineState != MachineState_Saved)
13340 {
13341 /*
13342 * delete the saved state file once the machine has finished
13343 * restoring from it (note that Console sets the state from
13344 * Restoring to Saved if the VM couldn't restore successfully,
13345 * to give the user an ability to fix an error and retry --
13346 * we keep the saved state file in this case)
13347 */
13348 deleteSavedState = true;
13349 }
13350 }
13351 else if ( oldMachineState == MachineState_Saved
13352 && ( aMachineState == MachineState_PoweredOff
13353 || aMachineState == MachineState_Aborted
13354 || aMachineState == MachineState_Teleported
13355 )
13356 )
13357 {
13358 /*
13359 * delete the saved state after Console::ForgetSavedState() is called
13360 * or if the VM process (owning a direct VM session) crashed while the
13361 * VM was Saved
13362 */
13363
13364 /// @todo (dmik)
13365 // Not sure that deleting the saved state file just because of the
13366 // client death before it attempted to restore the VM is a good
13367 // thing. But when it crashes we need to go to the Aborted state
13368 // which cannot have the saved state file associated... The only
13369 // way to fix this is to make the Aborted condition not a VM state
13370 // but a bool flag: i.e., when a crash occurs, set it to true and
13371 // change the state to PoweredOff or Saved depending on the
13372 // saved state presence.
13373
13374 deleteSavedState = true;
13375 mData->mCurrentStateModified = TRUE;
13376 stsFlags |= SaveSTS_CurStateModified;
13377 }
13378
13379 if ( aMachineState == MachineState_Starting
13380 || aMachineState == MachineState_Restoring
13381 || aMachineState == MachineState_TeleportingIn
13382 )
13383 {
13384 /* set the current state modified flag to indicate that the current
13385 * state is no more identical to the state in the
13386 * current snapshot */
13387 if (!mData->mCurrentSnapshot.isNull())
13388 {
13389 mData->mCurrentStateModified = TRUE;
13390 stsFlags |= SaveSTS_CurStateModified;
13391 }
13392 }
13393
13394 if (deleteSavedState)
13395 {
13396 if (mRemoveSavedState)
13397 {
13398 Assert(!mSSData->strStateFilePath.isEmpty());
13399
13400 // it is safe to delete the saved state file if ...
13401 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13402 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13403 // ... none of the snapshots share the saved state file
13404 )
13405 RTFileDelete(mSSData->strStateFilePath.c_str());
13406 }
13407
13408 mSSData->strStateFilePath.setNull();
13409 stsFlags |= SaveSTS_StateFilePath;
13410 }
13411
13412 /* redirect to the underlying peer machine */
13413 mPeer->setMachineState(aMachineState);
13414
13415 if ( aMachineState == MachineState_PoweredOff
13416 || aMachineState == MachineState_Teleported
13417 || aMachineState == MachineState_Aborted
13418 || aMachineState == MachineState_Saved)
13419 {
13420 /* the machine has stopped execution
13421 * (or the saved state file was adopted) */
13422 stsFlags |= SaveSTS_StateTimeStamp;
13423 }
13424
13425 if ( ( oldMachineState == MachineState_PoweredOff
13426 || oldMachineState == MachineState_Aborted
13427 || oldMachineState == MachineState_Teleported
13428 )
13429 && aMachineState == MachineState_Saved)
13430 {
13431 /* the saved state file was adopted */
13432 Assert(!mSSData->strStateFilePath.isEmpty());
13433 stsFlags |= SaveSTS_StateFilePath;
13434 }
13435
13436#ifdef VBOX_WITH_GUEST_PROPS
13437 if ( aMachineState == MachineState_PoweredOff
13438 || aMachineState == MachineState_Aborted
13439 || aMachineState == MachineState_Teleported)
13440 {
13441 /* Make sure any transient guest properties get removed from the
13442 * property store on shutdown. */
13443
13444 HWData::GuestPropertyList::iterator it;
13445 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13446 if (!fNeedsSaving)
13447 for (it = mHWData->mGuestProperties.begin();
13448 it != mHWData->mGuestProperties.end(); ++it)
13449 if ( (it->mFlags & guestProp::TRANSIENT)
13450 || (it->mFlags & guestProp::TRANSRESET))
13451 {
13452 fNeedsSaving = true;
13453 break;
13454 }
13455 if (fNeedsSaving)
13456 {
13457 mData->mCurrentStateModified = TRUE;
13458 stsFlags |= SaveSTS_CurStateModified;
13459 }
13460 }
13461#endif
13462
13463 rc = saveStateSettings(stsFlags);
13464
13465 if ( ( oldMachineState != MachineState_PoweredOff
13466 && oldMachineState != MachineState_Aborted
13467 && oldMachineState != MachineState_Teleported
13468 )
13469 && ( aMachineState == MachineState_PoweredOff
13470 || aMachineState == MachineState_Aborted
13471 || aMachineState == MachineState_Teleported
13472 )
13473 )
13474 {
13475 /* we've been shut down for any reason */
13476 /* no special action so far */
13477 }
13478
13479 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13480 LogFlowThisFuncLeave();
13481 return rc;
13482}
13483
13484/**
13485 * Sends the current machine state value to the VM process.
13486 *
13487 * @note Locks this object for reading, then calls a client process.
13488 */
13489HRESULT SessionMachine::updateMachineStateOnClient()
13490{
13491 AutoCaller autoCaller(this);
13492 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13493
13494 ComPtr<IInternalSessionControl> directControl;
13495 {
13496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13497 AssertReturn(!!mData, E_FAIL);
13498 directControl = mData->mSession.mDirectControl;
13499
13500 /* directControl may be already set to NULL here in #OnSessionEnd()
13501 * called too early by the direct session process while there is still
13502 * some operation (like deleting the snapshot) in progress. The client
13503 * process in this case is waiting inside Session::close() for the
13504 * "end session" process object to complete, while #uninit() called by
13505 * #checkForDeath() on the Watcher thread is waiting for the pending
13506 * operation to complete. For now, we accept this inconsistent behavior
13507 * and simply do nothing here. */
13508
13509 if (mData->mSession.mState == SessionState_Unlocking)
13510 return S_OK;
13511
13512 AssertReturn(!directControl.isNull(), E_FAIL);
13513 }
13514
13515 return directControl->UpdateMachineState(mData->mMachineState);
13516}
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