VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/RecordingSettingsImpl.cpp@ 78072

Last change on this file since 78072 was 78072, checked in by vboxsync, 6 years ago

Recording/Main: Settings locking fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1/* $Id: RecordingSettingsImpl.cpp 78072 2019-04-10 10:04:06Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation - Machine capture settings.
5 */
6
7/*
8 * Copyright (C) 2018-2019 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#define LOG_GROUP LOG_GROUP_MAIN_RECORDINGSETTINGS
20#include "LoggingNew.h"
21
22#include "RecordingSettingsImpl.h"
23#include "RecordingScreenSettingsImpl.h"
24#include "MachineImpl.h"
25
26#include <iprt/cpp/utils.h>
27#include <VBox/settings.h>
28
29#include "AutoStateDep.h"
30#include "AutoCaller.h"
31#include "Global.h"
32
33////////////////////////////////////////////////////////////////////////////////
34//
35// RecordSettings private data definition
36//
37////////////////////////////////////////////////////////////////////////////////
38
39struct RecordingSettings::Data
40{
41 Data()
42 : pMachine(NULL)
43 { }
44
45 Machine * const pMachine;
46 const ComObjPtr<RecordingSettings> pPeer;
47 RecordScreenSettingsMap mapScreenObj;
48
49 // use the XML settings structure in the members for simplicity
50 Backupable<settings::RecordingSettings> bd;
51};
52
53DEFINE_EMPTY_CTOR_DTOR(RecordingSettings)
54
55HRESULT RecordingSettings::FinalConstruct()
56{
57 return BaseFinalConstruct();
58}
59
60void RecordingSettings::FinalRelease()
61{
62 uninit();
63 BaseFinalRelease();
64}
65
66/**
67 * Initializes the recording settings object.
68 *
69 * @returns COM result indicator
70 */
71HRESULT RecordingSettings::init(Machine *aParent)
72{
73 LogFlowThisFuncEnter();
74 LogFlowThisFunc(("aParent: %p\n", aParent));
75
76 ComAssertRet(aParent, E_INVALIDARG);
77
78 /* Enclose the state transition NotReady->InInit->Ready */
79 AutoInitSpan autoInitSpan(this);
80 AssertReturn(autoInitSpan.isOk(), E_FAIL);
81
82 m = new Data();
83
84 /* share the parent weakly */
85 unconst(m->pMachine) = aParent;
86
87 m->bd.allocate();
88
89 autoInitSpan.setSucceeded();
90
91 LogFlowThisFuncLeave();
92 return S_OK;
93}
94
95/**
96 * Initializes the capture settings object given another capture settings object
97 * (a kind of copy constructor). This object shares data with
98 * the object passed as an argument.
99 *
100 * @note This object must be destroyed before the original object
101 * it shares data with is destroyed.
102 *
103 * @note Locks @a aThat object for reading.
104 */
105HRESULT RecordingSettings::init(Machine *aParent, RecordingSettings *aThat)
106{
107 LogFlowThisFuncEnter();
108 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
109
110 ComAssertRet(aParent && aThat, E_INVALIDARG);
111
112 /* Enclose the state transition NotReady->InInit->Ready */
113 AutoInitSpan autoInitSpan(this);
114 AssertReturn(autoInitSpan.isOk(), E_FAIL);
115
116 m = new Data();
117
118 unconst(m->pMachine) = aParent;
119 unconst(m->pPeer) = aThat;
120
121 AutoCaller thatCaller(aThat);
122 AssertComRCReturnRC(thatCaller.rc());
123
124 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
125
126 m->bd.share(aThat->m->bd);
127 m->mapScreenObj = aThat->m->mapScreenObj;
128
129 autoInitSpan.setSucceeded();
130
131 LogFlowThisFuncLeave();
132 return S_OK;
133}
134
135/**
136 * Initializes the guest object given another guest object
137 * (a kind of copy constructor). This object makes a private copy of data
138 * of the original object passed as an argument.
139 *
140 * @note Locks @a aThat object for reading.
141 */
142HRESULT RecordingSettings::initCopy(Machine *aParent, RecordingSettings *aThat)
143{
144 LogFlowThisFuncEnter();
145 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
146
147 ComAssertRet(aParent && aThat, E_INVALIDARG);
148
149 /* Enclose the state transition NotReady->InInit->Ready */
150 AutoInitSpan autoInitSpan(this);
151 AssertReturn(autoInitSpan.isOk(), E_FAIL);
152
153 m = new Data();
154
155 unconst(m->pMachine) = aParent;
156 // mPeer is left null
157
158 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
159
160 m->bd.attachCopy(aThat->m->bd);
161 m->mapScreenObj = aThat->m->mapScreenObj;
162
163 autoInitSpan.setSucceeded();
164
165 LogFlowThisFuncLeave();
166 return S_OK;
167}
168
169/**
170 * Uninitializes the instance and sets the ready flag to FALSE.
171 * Called either from FinalRelease() or by the parent when it gets destroyed.
172 */
173void RecordingSettings::uninit()
174{
175 LogFlowThisFuncEnter();
176
177 /* Enclose the state transition Ready->InUninit->NotReady */
178 AutoUninitSpan autoUninitSpan(this);
179 if (autoUninitSpan.uninitDone())
180 return;
181
182 /* Note: Do *not* call i_reset() here, as the shared recording configuration
183 * otherwise gets destructed when this object goes out of scope or is destroyed. */
184
185 m->bd.free();
186
187 unconst(m->pPeer) = NULL;
188 unconst(m->pMachine) = NULL;
189
190 delete m;
191 m = NULL;
192
193 LogFlowThisFuncLeave();
194}
195
196// IRecordSettings properties
197/////////////////////////////////////////////////////////////////////////////
198
199HRESULT RecordingSettings::getEnabled(BOOL *enabled)
200{
201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
202
203 *enabled = m->bd->fEnabled;
204
205 return S_OK;
206}
207
208HRESULT RecordingSettings::setEnabled(BOOL enable)
209{
210 /* the machine needs to be mutable */
211 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
212 if (FAILED(adep.rc())) return adep.rc();
213
214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
215
216 const bool fEnabled = RT_BOOL(enable);
217
218 HRESULT rc = S_OK;
219
220 if (m->bd->fEnabled != fEnabled)
221 {
222 m->bd.backup();
223 m->bd->fEnabled = fEnabled;
224
225 alock.release();
226
227 rc = m->pMachine->i_onRecordingChange(enable);
228 if (FAILED(rc))
229 {
230 /*
231 * Normally we would do the actual change _after_ i_onCaptureChange() succeeded.
232 * We cannot do this because that function uses RecordSettings::GetEnabled to
233 * determine if it should start or stop capturing. Therefore we need to manually
234 * undo change.
235 */
236 alock.acquire();
237 m->bd->fEnabled = m->bd.backedUpData()->fEnabled;
238 }
239 else
240 {
241 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
242 m->pMachine->i_setModified(Machine::IsModified_Recording);
243
244 /* Make sure to release the mutable dependency lock from above before
245 * actually saving the settings. */
246 adep.release();
247
248 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
249 if (Global::IsOnline(m->pMachine->i_getMachineState()))
250 rc = m->pMachine->i_saveSettings(NULL);
251 }
252 }
253
254 return rc;
255}
256
257HRESULT RecordingSettings::getScreens(std::vector<ComPtr<IRecordingScreenSettings> > &aRecordScreenSettings)
258{
259 LogFlowThisFuncEnter();
260
261 i_syncToMachineDisplays();
262
263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
264
265 aRecordScreenSettings.clear();
266 aRecordScreenSettings.resize(m->mapScreenObj.size());
267
268 RecordScreenSettingsMap::const_iterator itScreenSettings = m->mapScreenObj.begin();
269 size_t i = 0;
270 while (itScreenSettings != m->mapScreenObj.end())
271 {
272 itScreenSettings->second.queryInterfaceTo(aRecordScreenSettings[i].asOutParam());
273 Assert(aRecordScreenSettings[i].isNotNull());
274 ++i;
275 ++itScreenSettings;
276 }
277
278 Assert(aRecordScreenSettings.size() == m->mapScreenObj.size());
279
280 return S_OK;
281}
282
283HRESULT RecordingSettings::getScreenSettings(ULONG uScreenId, ComPtr<IRecordingScreenSettings> &aRecordScreenSettings)
284{
285 LogFlowThisFuncEnter();
286
287 i_syncToMachineDisplays();
288
289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
290
291 if (uScreenId + 1 > m->mapScreenObj.size())
292 return setError(E_INVALIDARG, tr("Invalid screen ID specified"));
293
294 RecordScreenSettingsMap::const_iterator itScreenSettings = m->mapScreenObj.find(uScreenId);
295 if (itScreenSettings != m->mapScreenObj.end())
296 {
297 itScreenSettings->second.queryInterfaceTo(aRecordScreenSettings.asOutParam());
298 return S_OK;
299 }
300
301 return VBOX_E_OBJECT_NOT_FOUND;
302}
303
304// IRecordSettings methods
305/////////////////////////////////////////////////////////////////////////////
306
307// public methods only for internal purposes
308/////////////////////////////////////////////////////////////////////////////
309
310/**
311 * Adds a screen settings object to a particular map.
312 *
313 * @returns IPRT status code. VERR_ALREADY_EXISTS if the object in question already exists.
314 * @param screenSettingsMap Map to add screen settings to.
315 * @param uScreenId Screen ID to add settings for.
316 * @param data Recording screen settings to use for that screen.
317 */
318int RecordingSettings::i_createScreenObj(RecordScreenSettingsMap &screenSettingsMap,
319 uint32_t uScreenId, const settings::RecordingScreenSettings &data)
320{
321 LogFlowThisFunc(("Screen %RU32\n", uScreenId));
322
323 if (screenSettingsMap.find(uScreenId) != screenSettingsMap.end())
324 {
325 AssertFailed();
326 return VERR_ALREADY_EXISTS;
327 }
328
329 int vrc = VINF_SUCCESS;
330
331 ComObjPtr<RecordingScreenSettings> recordingScreenSettings;
332 HRESULT rc = recordingScreenSettings.createObject();
333 if (SUCCEEDED(rc))
334 {
335 rc = recordingScreenSettings->init(this, uScreenId, data);
336 if (SUCCEEDED(rc))
337 {
338 try
339 {
340 screenSettingsMap[uScreenId] = recordingScreenSettings;
341 }
342 catch (std::bad_alloc &)
343 {
344 vrc = VERR_NO_MEMORY;
345 }
346 }
347 }
348
349 return vrc;
350}
351
352/**
353 * Removes a screen settings object from a particular map.
354 *
355 * @returns IPRT status code. VERR_NOT_FOUND if specified screen was not found.
356 * @param screenSettingsMap Map to remove screen settings from.
357 * @param uScreenId ID of screen to remove.
358 */
359int RecordingSettings::i_destroyScreenObj(RecordScreenSettingsMap &screenSettingsMap, uint32_t uScreenId)
360{
361 LogFlowThisFunc(("Screen %RU32\n", uScreenId));
362
363 AssertReturn(uScreenId > 0, VERR_INVALID_PARAMETER); /* Removing screen 0 isn't a good idea. */
364
365 RecordScreenSettingsMap::iterator itScreen = screenSettingsMap.find(uScreenId);
366 if (itScreen == screenSettingsMap.end())
367 {
368 AssertFailed();
369 return VERR_NOT_FOUND;
370 }
371
372 /* Make sure to consume the pointer before the one of the
373 * iterator gets released. */
374 ComObjPtr<RecordingScreenSettings> pScreenSettings = itScreen->second;
375
376 screenSettingsMap.erase(itScreen);
377
378 pScreenSettings.setNull();
379
380 return VINF_SUCCESS;
381}
382
383/**
384 * Destroys all screen settings objects of a particular map.
385 *
386 * @returns IPRT status code.
387 * @param screenSettingsMap Map to destroy screen settings objects for.
388 */
389int RecordingSettings::i_destroyAllScreenObj(RecordScreenSettingsMap &screenSettingsMap)
390{
391 LogFlowThisFuncEnter();
392
393 RecordScreenSettingsMap::iterator itScreen = screenSettingsMap.begin();
394 while (itScreen != screenSettingsMap.end())
395 {
396 /* Make sure to consume the pointer before the one of the
397 * iterator gets released. */
398 ComObjPtr<RecordingScreenSettings> pScreenSettings = itScreen->second;
399
400 screenSettingsMap.erase(itScreen);
401
402 pScreenSettings.setNull();
403
404 itScreen = screenSettingsMap.begin();
405 }
406
407 Assert(screenSettingsMap.size() == 0);
408 return VINF_SUCCESS;
409}
410
411/**
412 * Loads settings from the given settings.
413 * May be called once right after this object creation.
414 *
415 * @param data Capture settings to load from.
416 *
417 * @note Locks this object for writing.
418 */
419HRESULT RecordingSettings::i_loadSettings(const settings::RecordingSettings &data)
420{
421 LogFlowThisFuncEnter();
422
423 AutoCaller autoCaller(this);
424 AssertComRCReturnRC(autoCaller.rc());
425
426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
427
428 HRESULT rc = S_OK;
429
430 i_reset();
431
432 LogFlowThisFunc(("Data has %zu screens\n", data.mapScreens.size()));
433
434 settings::RecordingScreenMap::const_iterator itScreen = data.mapScreens.begin();
435 while (itScreen != data.mapScreens.end())
436 {
437 int vrc = i_createScreenObj(m->mapScreenObj,
438 itScreen->first /* uScreenId */, itScreen->second /* Settings */);
439 if (RT_FAILURE(vrc))
440 {
441 rc = E_OUTOFMEMORY;
442 break;
443 }
444
445 ++itScreen;
446 }
447
448 if (FAILED(rc))
449 return rc;
450
451 ComAssertComRC(rc);
452 Assert(m->mapScreenObj.size() == data.mapScreens.size());
453
454 // simply copy
455 m->bd.assignCopy(&data);
456
457 LogFlowThisFunc(("Returning %Rhrc\n", rc));
458 return rc;
459}
460
461/**
462 * Resets the internal object state by destroying all screen settings objects.
463 */
464void RecordingSettings::i_reset(void)
465{
466 LogFlowThisFuncEnter();
467
468 i_destroyAllScreenObj(m->mapScreenObj);
469 m->bd->mapScreens.clear();
470}
471
472/**
473 * Saves settings to the given settings.
474 *
475 * @param data Where to store the capture settings to.
476 *
477 * @note Locks this object for reading.
478 */
479HRESULT RecordingSettings::i_saveSettings(settings::RecordingSettings &data)
480{
481 LogFlowThisFuncEnter();
482
483 AutoCaller autoCaller(this);
484 AssertComRCReturnRC(autoCaller.rc());
485
486 int rc2 = i_syncToMachineDisplays();
487 AssertRC(rc2);
488
489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
490
491 data = *m->bd.data();
492
493 settings::RecordingScreenMap::iterator itScreen = data.mapScreens.begin();
494 while (itScreen != data.mapScreens.end())
495 {
496 /* Store relative path of capture file if possible. */
497 m->pMachine->i_copyPathRelativeToMachine(itScreen->second.File.strName /* Source */,
498 itScreen->second.File.strName /* Target */);
499 ++itScreen;
500 }
501
502 LogFlowThisFuncLeave();
503 return S_OK;
504}
505
506void RecordingSettings::i_rollback()
507{
508 /* sanity */
509 AutoCaller autoCaller(this);
510 AssertComRCReturnVoid(autoCaller.rc());
511
512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
513 m->bd.rollback();
514}
515
516void RecordingSettings::i_commit()
517{
518 /* sanity */
519 AutoCaller autoCaller(this);
520 AssertComRCReturnVoid(autoCaller.rc());
521
522 /* sanity too */
523 AutoCaller peerCaller(m->pPeer);
524 AssertComRCReturnVoid(peerCaller.rc());
525
526 /* lock both for writing since we modify both (mPeer is "master" so locked
527 * first) */
528 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
529
530 if (m->bd.isBackedUp())
531 {
532 m->bd.commit();
533 if (m->pPeer)
534 {
535 /* attach new data to the peer and reshare it */
536 m->pPeer->m->bd.attach(m->bd);
537 }
538 }
539}
540
541void RecordingSettings::i_copyFrom(RecordingSettings *aThat)
542{
543 AssertReturnVoid(aThat != NULL);
544
545 /* sanity */
546 AutoCaller autoCaller(this);
547 AssertComRCReturnVoid(autoCaller.rc());
548
549 /* sanity too */
550 AutoCaller thatCaller(aThat);
551 AssertComRCReturnVoid(thatCaller.rc());
552
553 /* peer is not modified, lock it for reading (aThat is "master" so locked
554 * first) */
555 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
556 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
557
558 /* this will back up current data */
559 m->bd.assignCopy(aThat->m->bd);
560}
561
562void RecordingSettings::i_applyDefaults(void)
563{
564 /* sanity */
565 AutoCaller autoCaller(this);
566 AssertComRCReturnVoid(autoCaller.rc());
567
568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
569
570 /* Initialize default capturing settings here. */
571 m->bd->fEnabled = false;
572
573 /* First, do a reset so that all internal screen settings objects are destroyed. */
574 i_reset();
575 /* Second, sync (again) to configured machine displays to (re-)create screen settings objects. */
576 i_syncToMachineDisplays();
577}
578
579/**
580 * Returns the full path to the default video capture file.
581 */
582int RecordingSettings::i_getDefaultFilename(Utf8Str &strFile, bool fWithFileExtension)
583{
584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
585
586 strFile = m->pMachine->i_getSettingsFileFull(); // path/to/machinesfolder/vmname/vmname.vbox
587 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
588 if (fWithFileExtension)
589 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
590
591 return VINF_SUCCESS;
592}
593
594/**
595 * Determines whether the recording settings currently can be changed or not.
596 *
597 * @returns \c true if the settings can be changed, \c false if not.
598 */
599bool RecordingSettings::i_canChangeSettings(void)
600{
601 AutoAnyStateDependency adep(m->pMachine);
602 if (FAILED(adep.rc()))
603 return false;
604
605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
606
607 /* Only allow settings to be changed when recording is disabled when the machine is running. */
608 if ( Global::IsOnline(adep.machineState())
609 && m->bd->fEnabled)
610 {
611 return false;
612 }
613
614 return true;
615}
616
617/**
618 * Gets called when the machine object needs to know that the recording settings
619 * have been changed.
620 */
621void RecordingSettings::i_onSettingsChanged(void)
622{
623 LogFlowThisFuncEnter();
624
625 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
626 m->pMachine->i_setModified(Machine::IsModified_Recording);
627 mlock.release();
628
629 LogFlowThisFuncLeave();
630}
631
632/**
633 * Synchronizes the screen settings (COM) objects and configuration data
634 * to the number of the machine's configured displays.
635 */
636int RecordingSettings::i_syncToMachineDisplays(void)
637{
638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
639
640 AssertPtr(m->pMachine);
641 const ULONG cMonitors = m->pMachine->i_getMonitorCount();
642
643 LogFlowThisFunc(("cMonitors=%RU32\n", cMonitors));
644 LogFlowThisFunc(("Data screen count = %zu, COM object count = %zu\n", m->bd->mapScreens.size(), m->mapScreenObj.size()));
645
646 /* If counts match, take a shortcut. */
647 if (cMonitors == m->mapScreenObj.size())
648 return VINF_SUCCESS;
649
650 /* Create all new screen settings objects which are not there yet. */
651 for (ULONG i = 0; i < cMonitors; i++)
652 {
653 if (m->mapScreenObj.find(i) == m->mapScreenObj.end())
654 {
655 settings::RecordingScreenMap::const_iterator itScreen = m->bd->mapScreens.find(i);
656 if (itScreen == m->bd->mapScreens.end())
657 {
658 settings::RecordingScreenSettings defaultScreenSettings; /* Apply default settings. */
659 m->bd->mapScreens[i] = defaultScreenSettings;
660 }
661
662 int vrc2 = i_createScreenObj(m->mapScreenObj, i /* Screen ID */, m->bd->mapScreens[i]);
663 AssertRC(vrc2);
664 }
665 }
666
667 /* Remove all left over screen settings objects which are not needed anymore. */
668 const ULONG cSettings = (ULONG)m->mapScreenObj.size();
669 for (ULONG i = cMonitors; i < cSettings; i++)
670 {
671 m->bd->mapScreens.erase(i);
672 int vrc2 = i_destroyScreenObj(m->mapScreenObj, i /* Screen ID */);
673 AssertRC(vrc2);
674 }
675
676 Assert(m->mapScreenObj.size() == cMonitors);
677 Assert(m->bd->mapScreens.size() == cMonitors);
678
679 LogFlowThisFuncLeave();
680 return VINF_SUCCESS;
681}
682
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette