VirtualBox

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

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

Runtime: new guest OS type for Solaris 11
Frontends/VirtualBox: add new patterns for Solaris 11 guest OS type, reuse the icon
Frontends/VBoxManage: more details for "list ostypes"
Main/xml: make guest OS type in config file an arbitrary string (still validated/mapped in the old way in the settings code), remove hardcoded limit of 8 network adapters
Main/Global: move list of valid guest OS types into a single place, add function to get the network adapter limit for each chipset type
Main/Console+Machine+Snapshot+NetworkAdapter+Appliance+VirtualBox+Guest+SystemProperties: consistently use the appropriate network adapter limit so that ICH9 chipset can use 36 network adapters, adapt to cleaned up guest OS type handling, remove leftover rendundant guest OS mapping, whitespace
Network/NAT: release log message cosmetics, allow unlimited number of instances, fix maxconn clamping
Network/PCNet+VirtioNet+E1000: allow unlimited number of instances

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