VirtualBox

source: vbox/trunk/src/VBox/Main/BIOSSettingsImpl.cpp@ 14683

Last change on this file since 14683 was 14665, checked in by vboxsync, 16 years ago

Main: Locking and sanity.

  • Property svn:eol-style set to native
File size: 19.8 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "BIOSSettingsImpl.h"
23#include "MachineImpl.h"
24#include "Logging.h"
25#include "GuestOSTypeImpl.h"
26#include <iprt/cpputils.h>
27
28// constructor / destructor
29/////////////////////////////////////////////////////////////////////////////
30
31HRESULT BIOSSettings::FinalConstruct()
32{
33 return S_OK;
34}
35
36void BIOSSettings::FinalRelease()
37{
38 uninit ();
39}
40
41// public initializer/uninitializer for internal purposes only
42/////////////////////////////////////////////////////////////////////////////
43
44/**
45 * Initializes the audio adapter object.
46 *
47 * @returns COM result indicator
48 */
49HRESULT BIOSSettings::init (Machine *aParent)
50{
51 LogFlowThisFuncEnter();
52 LogFlowThisFunc (("aParent: %p\n", aParent));
53
54 ComAssertRet (aParent, E_INVALIDARG);
55
56 /* Enclose the state transition NotReady->InInit->Ready */
57 AutoInitSpan autoInitSpan (this);
58 AssertReturn (autoInitSpan.isOk(), E_FAIL);
59
60 /* share the parent weakly */
61 unconst (mParent) = aParent;
62
63 mData.allocate();
64
65 autoInitSpan.setSucceeded();
66
67 LogFlowThisFuncLeave();
68 return S_OK;
69}
70
71/**
72 * Initializes the audio adapter object given another audio adapter object
73 * (a kind of copy constructor). This object shares data with
74 * the object passed as an argument.
75 *
76 * @note This object must be destroyed before the original object
77 * it shares data with is destroyed.
78 */
79HRESULT BIOSSettings::init (Machine *aParent, BIOSSettings *that)
80{
81 LogFlowThisFuncEnter();
82 LogFlowThisFunc (("aParent: %p, that: %p\n", aParent, that));
83
84 ComAssertRet (aParent && that, E_INVALIDARG);
85
86 /* Enclose the state transition NotReady->InInit->Ready */
87 AutoInitSpan autoInitSpan (this);
88 AssertReturn (autoInitSpan.isOk(), E_FAIL);
89
90 mParent = aParent;
91 mPeer = that;
92
93 AutoWriteLock thatlock (that);
94 mData.share (that->mData);
95
96 autoInitSpan.setSucceeded();
97
98 LogFlowThisFuncLeave();
99 return S_OK;
100}
101
102/**
103 * Initializes the guest object given another guest object
104 * (a kind of copy constructor). This object makes a private copy of data
105 * of the original object passed as an argument.
106 */
107HRESULT BIOSSettings::initCopy (Machine *aParent, BIOSSettings *that)
108{
109 LogFlowThisFuncEnter();
110 LogFlowThisFunc (("aParent: %p, that: %p\n", aParent, that));
111
112 ComAssertRet (aParent && that, E_INVALIDARG);
113
114 /* Enclose the state transition NotReady->InInit->Ready */
115 AutoInitSpan autoInitSpan (this);
116 AssertReturn (autoInitSpan.isOk(), E_FAIL);
117
118 mParent = aParent;
119 // mPeer is left null
120
121 AutoWriteLock thatlock (that);
122 mData.attachCopy (that->mData);
123
124 autoInitSpan.setSucceeded();
125
126 LogFlowThisFuncLeave();
127 return S_OK;
128}
129
130/**
131 * Uninitializes the instance and sets the ready flag to FALSE.
132 * Called either from FinalRelease() or by the parent when it gets destroyed.
133 */
134void BIOSSettings::uninit()
135{
136 LogFlowThisFuncEnter();
137
138 /* Enclose the state transition Ready->InUninit->NotReady */
139 AutoUninitSpan autoUninitSpan (this);
140 if (autoUninitSpan.uninitDone())
141 return;
142
143 mData.free();
144
145 mPeer.setNull();
146 mParent.setNull();
147
148 LogFlowThisFuncLeave();
149}
150
151// IBIOSSettings properties
152/////////////////////////////////////////////////////////////////////////////
153
154STDMETHODIMP BIOSSettings::COMGETTER(LogoFadeIn)(BOOL *enabled)
155{
156 if (!enabled)
157 return E_POINTER;
158
159 AutoCaller autoCaller (this);
160 CheckComRCReturnRC (autoCaller.rc());
161
162 AutoReadLock alock (this);
163
164 *enabled = mData->mLogoFadeIn;
165
166 return S_OK;
167}
168
169STDMETHODIMP BIOSSettings::COMSETTER(LogoFadeIn)(BOOL enable)
170{
171 AutoCaller autoCaller (this);
172 CheckComRCReturnRC (autoCaller.rc());
173
174 /* the machine needs to be mutable */
175 Machine::AutoMutableStateDependency adep (mParent);
176 CheckComRCReturnRC (adep.rc());
177
178 AutoWriteLock alock (this);
179
180 mData.backup();
181 mData->mLogoFadeIn = enable;
182
183 return S_OK;
184}
185
186STDMETHODIMP BIOSSettings::COMGETTER(LogoFadeOut)(BOOL *enabled)
187{
188 if (!enabled)
189 return E_POINTER;
190
191 AutoCaller autoCaller (this);
192 CheckComRCReturnRC (autoCaller.rc());
193
194 AutoReadLock alock (this);
195
196 *enabled = mData->mLogoFadeOut;
197
198 return S_OK;
199}
200
201STDMETHODIMP BIOSSettings::COMSETTER(LogoFadeOut)(BOOL enable)
202{
203 AutoCaller autoCaller (this);
204 CheckComRCReturnRC (autoCaller.rc());
205
206 /* the machine needs to be mutable */
207 Machine::AutoMutableStateDependency adep (mParent);
208 CheckComRCReturnRC (adep.rc());
209
210 AutoWriteLock alock (this);
211
212 mData.backup();
213 mData->mLogoFadeOut = enable;
214
215 return S_OK;
216}
217
218STDMETHODIMP BIOSSettings::COMGETTER(LogoDisplayTime)(ULONG *displayTime)
219{
220 if (!displayTime)
221 return E_POINTER;
222
223 AutoCaller autoCaller (this);
224 CheckComRCReturnRC (autoCaller.rc());
225
226 AutoReadLock alock (this);
227
228 *displayTime = mData->mLogoDisplayTime;
229
230 return S_OK;
231}
232
233STDMETHODIMP BIOSSettings::COMSETTER(LogoDisplayTime)(ULONG displayTime)
234{
235 AutoCaller autoCaller (this);
236 CheckComRCReturnRC (autoCaller.rc());
237
238 /* the machine needs to be mutable */
239 Machine::AutoMutableStateDependency adep (mParent);
240 CheckComRCReturnRC (adep.rc());
241
242 AutoWriteLock alock (this);
243
244 mData.backup();
245 mData->mLogoDisplayTime = displayTime;
246
247 return S_OK;
248}
249
250STDMETHODIMP BIOSSettings::COMGETTER(LogoImagePath)(BSTR *imagePath)
251{
252 if (!imagePath)
253 return E_POINTER;
254
255 AutoCaller autoCaller (this);
256 CheckComRCReturnRC (autoCaller.rc());
257
258 AutoReadLock alock (this);
259
260 mData->mLogoImagePath.cloneTo(imagePath);
261 return S_OK;
262}
263
264STDMETHODIMP BIOSSettings::COMSETTER(LogoImagePath)(INPTR BSTR imagePath)
265{
266 /* empty strings are not allowed as path names */
267 if (imagePath && !(*imagePath))
268 return E_INVALIDARG;
269
270 AutoCaller autoCaller (this);
271 CheckComRCReturnRC (autoCaller.rc());
272
273 /* the machine needs to be mutable */
274 Machine::AutoMutableStateDependency adep (mParent);
275 CheckComRCReturnRC (adep.rc());
276
277 AutoWriteLock alock (this);
278
279 mData.backup();
280 mData->mLogoImagePath = imagePath;
281
282 return S_OK;
283}
284
285STDMETHODIMP BIOSSettings::COMGETTER(BootMenuMode)(BIOSBootMenuMode_T *bootMenuMode)
286{
287 if (!bootMenuMode)
288 return E_POINTER;
289
290 AutoCaller autoCaller (this);
291 CheckComRCReturnRC (autoCaller.rc());
292
293 AutoReadLock alock (this);
294
295 *bootMenuMode = mData->mBootMenuMode;
296 return S_OK;
297}
298
299STDMETHODIMP BIOSSettings::COMSETTER(BootMenuMode)(BIOSBootMenuMode_T bootMenuMode)
300{
301 AutoCaller autoCaller (this);
302 CheckComRCReturnRC (autoCaller.rc());
303
304 /* the machine needs to be mutable */
305 Machine::AutoMutableStateDependency adep (mParent);
306 CheckComRCReturnRC (adep.rc());
307
308 AutoWriteLock alock (this);
309
310 mData.backup();
311 mData->mBootMenuMode = bootMenuMode;
312
313 return S_OK;
314}
315
316STDMETHODIMP BIOSSettings::COMGETTER(ACPIEnabled)(BOOL *enabled)
317{
318 if (!enabled)
319 return E_POINTER;
320
321 AutoCaller autoCaller (this);
322 CheckComRCReturnRC (autoCaller.rc());
323
324 AutoReadLock alock (this);
325
326 *enabled = mData->mACPIEnabled;
327
328 return S_OK;
329}
330
331STDMETHODIMP BIOSSettings::COMSETTER(ACPIEnabled)(BOOL enable)
332{
333 AutoCaller autoCaller (this);
334 CheckComRCReturnRC (autoCaller.rc());
335
336 /* the machine needs to be mutable */
337 Machine::AutoMutableStateDependency adep (mParent);
338 CheckComRCReturnRC (adep.rc());
339
340 AutoWriteLock alock (this);
341
342 mData.backup();
343 mData->mACPIEnabled = enable;
344
345 return S_OK;
346}
347
348STDMETHODIMP BIOSSettings::COMGETTER(IOAPICEnabled)(BOOL *enabled)
349{
350 if (!enabled)
351 return E_POINTER;
352
353 AutoCaller autoCaller (this);
354 CheckComRCReturnRC (autoCaller.rc());
355
356 AutoReadLock alock (this);
357
358 *enabled = mData->mIOAPICEnabled;
359
360 return S_OK;
361}
362
363STDMETHODIMP BIOSSettings::COMSETTER(IOAPICEnabled)(BOOL enable)
364{
365 AutoCaller autoCaller (this);
366 CheckComRCReturnRC (autoCaller.rc());
367
368 /* the machine needs to be mutable */
369 Machine::AutoMutableStateDependency adep (mParent);
370 CheckComRCReturnRC (adep.rc());
371
372 AutoWriteLock alock (this);
373
374 mData.backup();
375 mData->mIOAPICEnabled = enable;
376
377 return S_OK;
378}
379
380STDMETHODIMP BIOSSettings::COMGETTER(PXEDebugEnabled)(BOOL *enabled)
381{
382 if (!enabled)
383 return E_POINTER;
384
385 AutoCaller autoCaller (this);
386 CheckComRCReturnRC (autoCaller.rc());
387
388 AutoReadLock alock (this);
389
390 *enabled = mData->mPXEDebugEnabled;
391
392 return S_OK;
393}
394
395STDMETHODIMP BIOSSettings::COMSETTER(PXEDebugEnabled)(BOOL enable)
396{
397 AutoCaller autoCaller (this);
398 CheckComRCReturnRC (autoCaller.rc());
399
400 /* the machine needs to be mutable */
401 Machine::AutoMutableStateDependency adep (mParent);
402 CheckComRCReturnRC (adep.rc());
403
404 AutoWriteLock alock (this);
405
406 mData.backup();
407 mData->mPXEDebugEnabled = enable;
408
409 return S_OK;
410}
411
412STDMETHODIMP BIOSSettings::COMGETTER(IDEControllerType)(IDEControllerType_T *aControllerType)
413{
414 if (!aControllerType)
415 return E_POINTER;
416
417 AutoCaller autoCaller (this);
418 CheckComRCReturnRC (autoCaller.rc());
419
420 AutoReadLock alock (this);
421
422 *aControllerType = mData->mIDEControllerType;
423
424 return S_OK;
425}
426
427STDMETHODIMP BIOSSettings::COMSETTER(IDEControllerType)(IDEControllerType_T aControllerType)
428{
429 AutoCaller autoCaller (this);
430 CheckComRCReturnRC (autoCaller.rc());
431
432 /* the machine needs to be mutable */
433 Machine::AutoMutableStateDependency adep (mParent);
434 CheckComRCReturnRC (adep.rc());
435
436 AutoWriteLock alock (this);
437
438 /* make sure the value is allowed */
439 switch (aControllerType)
440 {
441 case IDEControllerType_PIIX3:
442 case IDEControllerType_PIIX4:
443 break;
444 default:
445 return setError (E_INVALIDARG,
446 tr("Invalid IDE controller type '%d'"),
447 aControllerType);
448 }
449
450 mData.backup();
451
452 mData->mIDEControllerType = aControllerType;
453
454 return S_OK;
455}
456
457STDMETHODIMP BIOSSettings::COMGETTER(TimeOffset)(LONG64 *offset)
458{
459 if (!offset)
460 return E_POINTER;
461
462 AutoCaller autoCaller (this);
463 CheckComRCReturnRC (autoCaller.rc());
464
465 AutoReadLock alock (this);
466
467 *offset = mData->mTimeOffset;
468
469 return S_OK;
470}
471
472STDMETHODIMP BIOSSettings::COMSETTER(TimeOffset)(LONG64 offset)
473{
474 AutoCaller autoCaller (this);
475 CheckComRCReturnRC (autoCaller.rc());
476
477 /* the machine needs to be mutable */
478 Machine::AutoMutableStateDependency adep (mParent);
479 CheckComRCReturnRC (adep.rc());
480
481 AutoWriteLock alock (this);
482
483 mData.backup();
484 mData->mTimeOffset = offset;
485
486 return S_OK;
487}
488
489
490// IBIOSSettings methods
491/////////////////////////////////////////////////////////////////////////////
492
493// public methods only for internal purposes
494/////////////////////////////////////////////////////////////////////////////
495
496/**
497 * Loads settings from the given machine node.
498 * May be called once right after this object creation.
499 *
500 * @param aMachineNode <Machine> node.
501 *
502 * @note Locks this object for writing.
503 */
504HRESULT BIOSSettings::loadSettings (const settings::Key &aMachineNode)
505{
506 using namespace settings;
507
508 AssertReturn (!aMachineNode.isNull(), E_FAIL);
509
510 AutoCaller autoCaller (this);
511 AssertComRCReturnRC (autoCaller.rc());
512
513 AutoWriteLock alock (this);
514
515 /* Note: we assume that the default values for attributes of optional
516 * nodes are assigned in the Data::Data() constructor and don't do it
517 * here. It implies that this method may only be called after constructing
518 * a new BIOSSettings object while all its data fields are in the default
519 * values. Exceptions are fields whose creation time defaults don't match
520 * values that should be applied when these fields are not explicitly set
521 * in the settings file (for backwards compatibility reasons). This takes
522 * place when a setting of a newly created object must default to A while
523 * the same setting of an object loaded from the old settings file must
524 * default to B. */
525
526 /* BIOS node (required) */
527 Key biosNode = aMachineNode.key ("BIOS");
528
529 /* ACPI (required) */
530 {
531 Key acpiNode = biosNode.key ("ACPI");
532
533 mData->mACPIEnabled = acpiNode.value <bool> ("enabled");
534 }
535
536 /* IOAPIC (optional) */
537 {
538 Key ioapicNode = biosNode.findKey ("IOAPIC");
539 if (!ioapicNode.isNull())
540 mData->mIOAPICEnabled = ioapicNode.value <bool> ("enabled");
541 }
542
543 /* Logo (optional) */
544 {
545 Key logoNode = biosNode.findKey ("Logo");
546 if (!logoNode.isNull())
547 {
548 mData->mLogoFadeIn = logoNode.value <bool> ("fadeIn");
549 mData->mLogoFadeOut = logoNode.value <bool> ("fadeOut");
550 mData->mLogoDisplayTime = logoNode.value <ULONG> ("displayTime");
551 mData->mLogoImagePath = logoNode.stringValue ("imagePath");
552 }
553 }
554
555 /* boot menu (optional) */
556 {
557 Key bootMenuNode = biosNode.findKey ("BootMenu");
558 if (!bootMenuNode.isNull())
559 {
560 mData->mBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
561 const char *modeStr = bootMenuNode.stringValue ("mode");
562
563 if (strcmp (modeStr, "Disabled") == 0)
564 mData->mBootMenuMode = BIOSBootMenuMode_Disabled;
565 else if (strcmp (modeStr, "MenuOnly") == 0)
566 mData->mBootMenuMode = BIOSBootMenuMode_MenuOnly;
567 else if (strcmp (modeStr, "MessageAndMenu") == 0)
568 mData->mBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
569 else
570 ComAssertMsgFailedRet (("Invalid boot menu mode '%s'\n", modeStr),
571 E_FAIL);
572 }
573 }
574
575 /* PXE debug logging (optional) */
576 {
577 Key pxedebugNode = biosNode.findKey ("PXEDebug");
578 if (!pxedebugNode.isNull())
579 mData->mPXEDebugEnabled = pxedebugNode.value <bool> ("enabled");
580 }
581
582 /* time offset (optional) */
583 {
584 Key timeOffsetNode = biosNode.findKey ("TimeOffset");
585 if (!timeOffsetNode.isNull())
586 mData->mTimeOffset = timeOffsetNode.value <LONG64> ("value");
587 }
588
589 /* IDE controller type (optional, for old machines that lack this node,
590 * defaults to PIIX3) */
591 {
592 mData->mIDEControllerType = IDEControllerType_PIIX3;
593
594 Key ideControllerNode = biosNode.findKey ("IDEController");
595 if (!ideControllerNode.isNull())
596 {
597 const char *typeStr = ideControllerNode.stringValue ("type");
598 if (strcmp (typeStr, "PIIX3") == 0)
599 mData->mIDEControllerType = IDEControllerType_PIIX3;
600 else if (strcmp (typeStr, "PIIX4") == 0)
601 mData->mIDEControllerType = IDEControllerType_PIIX4;
602 else
603 ComAssertMsgFailedRet (("Invalid boot menu mode '%s'\n", typeStr),
604 E_FAIL);
605 }
606 }
607
608 return S_OK;
609}
610
611/**
612 * Saves settings to the given machine node.
613 *
614 * @param aMachineNode <Machine> node.
615 *
616 * @note Locks this object for reading.
617 */
618HRESULT BIOSSettings::saveSettings (settings::Key &aMachineNode)
619{
620 using namespace settings;
621
622 AssertReturn (!aMachineNode.isNull(), E_FAIL);
623
624 AutoCaller autoCaller (this);
625 AssertComRCReturnRC (autoCaller.rc());
626
627 AutoReadLock alock (this);
628
629 Key biosNode = aMachineNode.createKey ("BIOS");
630
631 /* ACPI */
632 {
633 Key acpiNode = biosNode.createKey ("ACPI");
634 acpiNode.setValue <bool> ("enabled", !!mData->mACPIEnabled);
635 }
636
637 /* IOAPIC */
638 {
639 Key ioapicNode = biosNode.createKey ("IOAPIC");
640 ioapicNode.setValue <bool> ("enabled", !!mData->mIOAPICEnabled);
641 }
642
643 /* BIOS logo (optional) **/
644 {
645 Key logoNode = biosNode.createKey ("Logo");
646 logoNode.setValue <bool> ("fadeIn", !!mData->mLogoFadeIn);
647 logoNode.setValue <bool> ("fadeOut", !!mData->mLogoFadeOut);
648 logoNode.setValue <ULONG> ("displayTime", mData->mLogoDisplayTime);
649 logoNode.setValueOr <Bstr> ("imagePath", mData->mLogoImagePath, Bstr::Null);
650 }
651
652 /* boot menu (optional) */
653 {
654 Key bootMenuNode = biosNode.createKey ("BootMenu");
655 const char *modeStr = NULL;
656 switch (mData->mBootMenuMode)
657 {
658 case BIOSBootMenuMode_Disabled:
659 modeStr = "Disabled";
660 break;
661 case BIOSBootMenuMode_MenuOnly:
662 modeStr = "MenuOnly";
663 break;
664 case BIOSBootMenuMode_MessageAndMenu:
665 modeStr = "MessageAndMenu";
666 break;
667 default:
668 ComAssertMsgFailedRet (("Invalid boot menu type: %d\n",
669 mData->mBootMenuMode),
670 E_FAIL);
671 }
672 bootMenuNode.setStringValue ("mode", modeStr);
673 }
674
675 /* time offset (optional) */
676 {
677 Key timeOffsetNode = biosNode.createKey ("TimeOffset");
678 timeOffsetNode.setValue <LONG64> ("value", mData->mTimeOffset);
679 }
680
681 /* PXE debug flag (optional) */
682 {
683 Key pxedebugNode = biosNode.createKey ("PXEDebug");
684 pxedebugNode.setValue <bool> ("enabled", !!mData->mPXEDebugEnabled);
685 }
686
687 /* IDE controller type */
688 {
689 Key ideControllerNode = biosNode.createKey ("IDEController");
690 const char *ideControllerTypeStr = NULL;
691 switch (mData->mIDEControllerType)
692 {
693 case IDEControllerType_PIIX3:
694 ideControllerTypeStr = "PIIX3";
695 break;
696 case IDEControllerType_PIIX4:
697 ideControllerTypeStr = "PIIX4";
698 break;
699 default:
700 ComAssertMsgFailedRet (("Invalid IDE Controller type: %d\n",
701 mData->mIDEControllerType),
702 E_FAIL);
703 }
704 ideControllerNode.setStringValue ("type", ideControllerTypeStr);
705 }
706
707 return S_OK;
708}
709
710void BIOSSettings::commit()
711{
712 /* sanity */
713 AutoCaller autoCaller (this);
714 AssertComRCReturnVoid (autoCaller.rc());
715
716 /* sanity too */
717 AutoCaller peerCaller (mPeer);
718 AssertComRCReturnVoid (peerCaller.rc());
719
720 /* lock both for writing since we modify both (mPeer is "master" so locked
721 * first) */
722 AutoMultiWriteLock2 alock (mPeer, this);
723
724 if (mData.isBackedUp())
725 {
726 mData.commit();
727 if (mPeer)
728 {
729 /* attach new data to the peer and reshare it */
730 AutoWriteLock peerlock (mPeer);
731 mPeer->mData.attach (mData);
732 }
733 }
734}
735
736void BIOSSettings::copyFrom (BIOSSettings *aThat)
737{
738 AssertReturnVoid (aThat != NULL);
739
740 /* sanity */
741 AutoCaller autoCaller (this);
742 AssertComRCReturnVoid (autoCaller.rc());
743
744 /* sanity too */
745 AutoCaller thatCaller (aThat);
746 AssertComRCReturnVoid (thatCaller.rc());
747
748 /* peer is not modified, lock it for reading (aThat is "master" so locked
749 * first) */
750 AutoMultiLock2 alock (aThat->rlock(), this->wlock());
751
752 /* this will back up current data */
753 mData.assignCopy (aThat->mData);
754}
755
756void BIOSSettings::applyDefaults (GuestOSType *aOsType)
757{
758 AssertReturnVoid (aOsType != NULL);
759
760 /* sanity */
761 AutoCaller autoCaller (this);
762 AssertComRCReturnVoid (autoCaller.rc());
763
764 AutoWriteLock alock (this);
765
766 /* Initialize default BIOS settings here */
767 mData->mIOAPICEnabled = aOsType->recommendedIOAPIC();
768}
769
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