VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.3 KB
Line 
1/* $Id: VBoxManageStorageController.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxManage - The storage controller related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <VBox/com/com.h>
33#include <VBox/com/array.h>
34#include <VBox/com/ErrorInfo.h>
35#include <VBox/com/errorprint.h>
36#include <VBox/com/VirtualBox.h>
37
38#include <iprt/path.h>
39#include <iprt/param.h>
40#include <iprt/string.h>
41#include <iprt/ctype.h>
42#include <iprt/stream.h>
43#include <iprt/getopt.h>
44#include <VBox/log.h>
45
46#include "VBoxManage.h"
47using namespace com;
48
49DECLARE_TRANSLATION_CONTEXT(Storage);
50
51// funcs
52///////////////////////////////////////////////////////////////////////////////
53
54
55static const RTGETOPTDEF g_aStorageAttachOptions[] =
56{
57 { "--storagectl", 's', RTGETOPT_REQ_STRING },
58 { "--port", 'p', RTGETOPT_REQ_UINT32 },
59 { "--device", 'd', RTGETOPT_REQ_UINT32 },
60 { "--type", 't', RTGETOPT_REQ_STRING },
61 { "--medium", 'm', RTGETOPT_REQ_STRING },
62 { "--mtype", 'M', RTGETOPT_REQ_STRING },
63 { "--passthrough", 'h', RTGETOPT_REQ_STRING },
64 { "--tempeject", 'e', RTGETOPT_REQ_STRING },
65 { "--nonrotational", 'n', RTGETOPT_REQ_STRING },
66 { "--discard", 'u', RTGETOPT_REQ_STRING },
67 { "--hotpluggable", 'o', RTGETOPT_REQ_STRING },
68 { "--bandwidthgroup", 'b', RTGETOPT_REQ_STRING },
69 { "--forceunmount", 'f', RTGETOPT_REQ_NOTHING },
70 { "--comment", 'C', RTGETOPT_REQ_STRING },
71 { "--setuuid", 'q', RTGETOPT_REQ_STRING },
72 { "--setparentuuid", 'Q', RTGETOPT_REQ_STRING },
73 // iSCSI options
74 { "--server", 'S', RTGETOPT_REQ_STRING },
75 { "--target", 'T', RTGETOPT_REQ_STRING },
76 { "--tport", 'P', RTGETOPT_REQ_STRING },
77 { "--lun", 'L', RTGETOPT_REQ_STRING },
78 { "--encodedlun", 'E', RTGETOPT_REQ_STRING },
79 { "--username", 'U', RTGETOPT_REQ_STRING },
80 { "--password", 'W', RTGETOPT_REQ_STRING },
81 { "--passwordfile", 'w', RTGETOPT_REQ_STRING },
82 { "--initiator", 'N', RTGETOPT_REQ_STRING },
83 { "--intnet", 'I', RTGETOPT_REQ_NOTHING },
84};
85
86RTEXITCODE handleStorageAttach(HandlerArg *a)
87{
88 int c = VERR_INTERNAL_ERROR; /* initialized to shut up gcc */
89 HRESULT hrc = S_OK;
90 ULONG port = ~0U;
91 ULONG device = ~0U;
92 bool fForceUnmount = false;
93 bool fSetMediumType = false;
94 bool fSetNewUuid = false;
95 bool fSetNewParentUuid = false;
96 MediumType_T enmMediumType = MediumType_Normal;
97 Bstr bstrComment;
98 const char *pszCtl = NULL;
99 DeviceType_T devTypeRequested = DeviceType_Null;
100 const char *pszMedium = NULL;
101 const char *pszPassThrough = NULL;
102 const char *pszTempEject = NULL;
103 const char *pszNonRotational = NULL;
104 const char *pszDiscard = NULL;
105 const char *pszHotPluggable = NULL;
106 const char *pszBandwidthGroup = NULL;
107 Bstr bstrNewUuid;
108 Bstr bstrNewParentUuid;
109 // iSCSI options
110 Bstr bstrServer;
111 Bstr bstrTarget;
112 Bstr bstrPort;
113 Bstr bstrLun;
114 Bstr bstrUsername;
115 Bstr bstrPassword;
116 Bstr bstrInitiator;
117 Bstr bstrIso;
118 Utf8Str strIso;
119 bool fIntNet = false;
120
121 RTGETOPTUNION ValueUnion;
122 RTGETOPTSTATE GetState;
123 ComPtr<IMachine> machine;
124 ComPtr<IStorageController> storageCtl;
125 ComPtr<ISystemProperties> systemProperties;
126
127 RTGetOptInit(&GetState, a->argc, a->argv, g_aStorageAttachOptions,
128 RT_ELEMENTS(g_aStorageAttachOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
129
130 while ( SUCCEEDED(hrc)
131 && (c = RTGetOpt(&GetState, &ValueUnion)))
132 {
133 switch (c)
134 {
135 case 's': // storage controller name
136 {
137 if (ValueUnion.psz)
138 pszCtl = ValueUnion.psz;
139 else
140 hrc = E_FAIL;
141 break;
142 }
143
144 case 'p': // port
145 {
146 port = ValueUnion.u32;
147 break;
148 }
149
150 case 'd': // device
151 {
152 device = ValueUnion.u32;
153 break;
154 }
155
156 case 'm': // medium <none|emptydrive|additions|uuid|filename|host:<drive>|iSCSI>
157 {
158 if (ValueUnion.psz)
159 pszMedium = ValueUnion.psz;
160 else
161 hrc = E_FAIL;
162 break;
163 }
164
165 case 't': // type <dvddrive|hdd|fdd>
166 {
167 if (ValueUnion.psz)
168 {
169 if (!RTStrICmp(ValueUnion.psz, "hdd"))
170 devTypeRequested = DeviceType_HardDisk;
171 else if (!RTStrICmp(ValueUnion.psz, "fdd"))
172 devTypeRequested = DeviceType_Floppy;
173 else if (!RTStrICmp(ValueUnion.psz, "dvddrive"))
174 devTypeRequested = DeviceType_DVD;
175 else
176 return errorArgument(Storage::tr("Invalid --type argument '%s'"), ValueUnion.psz);
177 }
178 else
179 hrc = E_FAIL;
180 break;
181 }
182
183 case 'h': // passthrough <on|off>
184 {
185 if (ValueUnion.psz)
186 pszPassThrough = ValueUnion.psz;
187 else
188 hrc = E_FAIL;
189 break;
190 }
191
192 case 'e': // tempeject <on|off>
193 {
194 if (ValueUnion.psz)
195 pszTempEject = ValueUnion.psz;
196 else
197 hrc = E_FAIL;
198 break;
199 }
200
201 case 'n': // nonrotational <on|off>
202 {
203 if (ValueUnion.psz)
204 pszNonRotational = ValueUnion.psz;
205 else
206 hrc = E_FAIL;
207 break;
208 }
209
210 case 'u': // discard <on|off>
211 {
212 if (ValueUnion.psz)
213 pszDiscard = ValueUnion.psz;
214 else
215 hrc = E_FAIL;
216 break;
217 }
218
219 case 'o': // hotpluggable <on|off>
220 {
221 if (ValueUnion.psz)
222 pszHotPluggable = ValueUnion.psz;
223 else
224 hrc = E_FAIL;
225 break;
226 }
227
228 case 'b': // bandwidthgroup <name>
229 {
230 if (ValueUnion.psz)
231 pszBandwidthGroup = ValueUnion.psz;
232 else
233 hrc = E_FAIL;
234 break;
235 }
236
237 case 'f': // force unmount medium during runtime
238 {
239 fForceUnmount = true;
240 break;
241 }
242
243 case 'C':
244 if (ValueUnion.psz)
245 bstrComment = ValueUnion.psz;
246 else
247 hrc = E_FAIL;
248 break;
249
250 case 'q':
251 if (ValueUnion.psz)
252 {
253 bstrNewUuid = ValueUnion.psz;
254 fSetNewUuid = true;
255 }
256 else
257 hrc = E_FAIL;
258 break;
259
260 case 'Q':
261 if (ValueUnion.psz)
262 {
263 bstrNewParentUuid = ValueUnion.psz;
264 fSetNewParentUuid = true;
265 }
266 else
267 hrc = E_FAIL;
268 break;
269
270 case 'S': // --server
271 bstrServer = ValueUnion.psz;
272 break;
273
274 case 'T': // --target
275 bstrTarget = ValueUnion.psz;
276 break;
277
278 case 'P': // --tport
279 bstrPort = ValueUnion.psz;
280 break;
281
282 case 'L': // --lun
283 bstrLun = ValueUnion.psz;
284 break;
285
286 case 'E': // --encodedlun
287 bstrLun = BstrFmt("enc%s", ValueUnion.psz);
288 break;
289
290 case 'U': // --username
291 bstrUsername = ValueUnion.psz;
292 break;
293
294 case 'W': // --password
295 bstrPassword = ValueUnion.psz;
296 break;
297
298 case 'w': // --passwordFile
299 {
300 Utf8Str utf8Password;
301 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &utf8Password);
302 if (rcExit != RTEXITCODE_SUCCESS)
303 hrc = E_FAIL;
304 bstrPassword = utf8Password;
305 break;
306 }
307 case 'N': // --initiator
308 bstrInitiator = ValueUnion.psz;
309 break;
310
311 case 'M': // --type
312 {
313 int vrc = parseMediumType(ValueUnion.psz, &enmMediumType);
314 if (RT_FAILURE(vrc))
315 return errorArgument(Storage::tr("Invalid medium type '%s'"), ValueUnion.psz);
316 fSetMediumType = true;
317 break;
318 }
319
320 case 'I': // --intnet
321 fIntNet = true;
322 break;
323
324 default:
325 {
326 errorGetOpt(c, &ValueUnion);
327 hrc = E_FAIL;
328 break;
329 }
330 }
331 }
332
333 if (FAILED(hrc))
334 return RTEXITCODE_FAILURE;
335
336 if (!pszCtl)
337 return errorSyntax(Storage::tr("Storage controller name not specified"));
338
339 /* get the virtualbox system properties */
340 CHECK_ERROR_RET(a->virtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()), RTEXITCODE_FAILURE);
341
342 // find the machine, lock it, get the mutable session machine
343 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
344 machine.asOutParam()), RTEXITCODE_FAILURE);
345 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
346 SessionType_T st;
347 CHECK_ERROR_RET(a->session, COMGETTER(Type)(&st), RTEXITCODE_FAILURE);
348 a->session->COMGETTER(Machine)(machine.asOutParam());
349
350 try
351 {
352 bool fRunTime = (st == SessionType_Shared);
353
354 if (fRunTime)
355 {
356 if (pszPassThrough)
357 throw Utf8Str(Storage::tr("Drive passthrough state cannot be changed while the VM is running\n"));
358 else if (pszBandwidthGroup)
359 throw Utf8Str(Storage::tr("Bandwidth group cannot be changed while the VM is running\n"));
360 }
361
362 /* check if the storage controller is present */
363 hrc = machine->GetStorageControllerByName(Bstr(pszCtl).raw(),
364 storageCtl.asOutParam());
365 if (FAILED(hrc))
366 throw Utf8StrFmt(Storage::tr("Could not find a controller named '%s'\n"), pszCtl);
367
368 StorageBus_T storageBus = StorageBus_Null;
369 CHECK_ERROR_RET(storageCtl, COMGETTER(Bus)(&storageBus), RTEXITCODE_FAILURE);
370 ULONG maxPorts = 0;
371 CHECK_ERROR_RET(systemProperties, GetMaxPortCountForStorageBus(storageBus, &maxPorts), RTEXITCODE_FAILURE);
372 ULONG maxDevices = 0;
373 CHECK_ERROR_RET(systemProperties, GetMaxDevicesPerPortForStorageBus(storageBus, &maxDevices), RTEXITCODE_FAILURE);
374
375 if (port == ~0U)
376 {
377 if (maxPorts == 1)
378 port = 0;
379 else
380 return errorSyntax(Storage::tr("Port not specified"));
381 }
382 if (device == ~0U)
383 {
384 if (maxDevices == 1)
385 device = 0;
386 else
387 return errorSyntax(Storage::tr("Device not specified"));
388 }
389
390 /* for sata controller check if the port count is big enough
391 * to accommodate the current port which is being assigned
392 * else just increase the port count
393 */
394 {
395 ULONG ulPortCount = 0;
396 ULONG ulMaxPortCount = 0;
397
398 CHECK_ERROR(storageCtl, COMGETTER(MaxPortCount)(&ulMaxPortCount));
399 CHECK_ERROR(storageCtl, COMGETTER(PortCount)(&ulPortCount));
400
401 if ( (ulPortCount != ulMaxPortCount)
402 && (port >= ulPortCount)
403 && (port < ulMaxPortCount))
404 CHECK_ERROR(storageCtl, COMSETTER(PortCount)(port + 1));
405 }
406
407 StorageControllerType_T ctlType = StorageControllerType_Null;
408 CHECK_ERROR(storageCtl, COMGETTER(ControllerType)(&ctlType));
409
410 if (!RTStrICmp(pszMedium, "none"))
411 {
412 CHECK_ERROR(machine, DetachDevice(Bstr(pszCtl).raw(), port, device));
413 }
414 else if (!RTStrICmp(pszMedium, "emptydrive"))
415 {
416 if (fRunTime)
417 {
418 ComPtr<IMediumAttachment> mediumAttachment;
419 DeviceType_T deviceType = DeviceType_Null;
420 hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port, device,
421 mediumAttachment.asOutParam());
422 if (SUCCEEDED(hrc))
423 {
424 mediumAttachment->COMGETTER(Type)(&deviceType);
425
426 if ( (deviceType == DeviceType_DVD)
427 || (deviceType == DeviceType_Floppy))
428 {
429 /* just unmount the floppy/dvd */
430 CHECK_ERROR(machine, UnmountMedium(Bstr(pszCtl).raw(),
431 port,
432 device,
433 fForceUnmount));
434 }
435 }
436 else if (devTypeRequested == DeviceType_DVD)
437 {
438 /*
439 * Try to attach an empty DVD drive as a hotplug operation.
440 * Main will complain if the controller doesn't support hotplugging.
441 */
442 CHECK_ERROR(machine, AttachDeviceWithoutMedium(Bstr(pszCtl).raw(), port, device,
443 devTypeRequested));
444 deviceType = DeviceType_DVD; /* To avoid the error message below. */
445 }
446
447 if ( FAILED(hrc)
448 || !( deviceType == DeviceType_DVD
449 || deviceType == DeviceType_Floppy)
450 )
451 throw Utf8StrFmt(Storage::tr("No DVD/Floppy Drive attached to the controller '%s'"
452 "at the port: %u, device: %u"), pszCtl, port, device);
453
454 }
455 else
456 {
457 DeviceType_T deviceType = DeviceType_Null;
458 com::SafeArray <DeviceType_T> saDeviceTypes;
459 ULONG driveCheck = 0;
460
461 /* check if the device type is supported by the controller */
462 CHECK_ERROR(systemProperties, GetDeviceTypesForStorageBus(storageBus, ComSafeArrayAsOutParam(saDeviceTypes)));
463 for (size_t i = 0; i < saDeviceTypes.size(); ++ i)
464 {
465 if ( (saDeviceTypes[i] == DeviceType_DVD)
466 || (saDeviceTypes[i] == DeviceType_Floppy))
467 driveCheck++;
468 }
469
470 if (!driveCheck)
471 throw Utf8StrFmt(Storage::tr("The attachment is not supported by the storage controller '%s'"), pszCtl);
472
473 if (storageBus == StorageBus_Floppy)
474 deviceType = DeviceType_Floppy;
475 else
476 deviceType = DeviceType_DVD;
477
478 /* attach a empty floppy/dvd drive after removing previous attachment */
479 machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
480 CHECK_ERROR(machine, AttachDeviceWithoutMedium(Bstr(pszCtl).raw(), port, device,
481 deviceType));
482 }
483 } // end if (!RTStrICmp(pszMedium, "emptydrive"))
484 else
485 {
486 ComPtr<IMedium> pMedium2Mount;
487
488 // not "none", not "emptydrive": then it must be a UUID or filename or hostdrive or iSCSI;
489 // for all these we first need to know the type of drive we're attaching to
490 {
491 /*
492 * try to determine the type of the drive from the
493 * storage controller chipset, the attachment and
494 * the medium being attached
495 */
496 if (ctlType == StorageControllerType_I82078) // floppy controller
497 devTypeRequested = DeviceType_Floppy;
498 else
499 {
500 /*
501 * for SATA/SCSI/IDE it is hard to tell if it is a harddisk or
502 * a dvd being attached so lets check if the medium attachment
503 * and the medium, both are of same type. if yes then we are
504 * sure of its type and don't need the user to enter it manually
505 * else ask the user for the type.
506 */
507 ComPtr<IMediumAttachment> mediumAttachment;
508 hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port,
509 device,
510 mediumAttachment.asOutParam());
511 if (SUCCEEDED(hrc))
512 {
513 DeviceType_T deviceType;
514 mediumAttachment->COMGETTER(Type)(&deviceType);
515
516 if (pszMedium)
517 {
518 if (!RTStrICmp(pszMedium, "additions"))
519 {
520 ComPtr<ISystemProperties> pProperties;
521 CHECK_ERROR(a->virtualBox,
522 COMGETTER(SystemProperties)(pProperties.asOutParam()));
523 CHECK_ERROR(pProperties, COMGETTER(DefaultAdditionsISO)(bstrIso.asOutParam()));
524 strIso = Utf8Str(bstrIso);
525 if (strIso.isEmpty())
526 throw Utf8Str(Storage::tr("Cannot find the Guest Additions ISO image\n"));
527 pszMedium = strIso.c_str();
528 if (devTypeRequested == DeviceType_Null)
529 devTypeRequested = DeviceType_DVD;
530 }
531 ComPtr<IMedium> pExistingMedium;
532 hrc = openMedium(a, pszMedium, deviceType,
533 AccessMode_ReadWrite,
534 pExistingMedium,
535 false /* fForceNewUuidOnOpen */,
536 true /* fSilent */);
537 if (SUCCEEDED(hrc) && pExistingMedium)
538 {
539 if ( (deviceType == DeviceType_DVD)
540 || (deviceType == DeviceType_HardDisk)
541 )
542 devTypeRequested = deviceType;
543 }
544 }
545 else
546 devTypeRequested = deviceType;
547 }
548 }
549 }
550
551 if (devTypeRequested == DeviceType_Null) // still the initializer value?
552 throw Utf8Str(Storage::tr("Argument --type must be specified\n"));
553
554 /* check if the device type is supported by the controller */
555 {
556 com::SafeArray <DeviceType_T> saDeviceTypes;
557
558 CHECK_ERROR(systemProperties, GetDeviceTypesForStorageBus(storageBus, ComSafeArrayAsOutParam(saDeviceTypes)));
559 if (SUCCEEDED(hrc))
560 {
561 ULONG driveCheck = 0;
562 for (size_t i = 0; i < saDeviceTypes.size(); ++ i)
563 if (saDeviceTypes[i] == devTypeRequested)
564 driveCheck++;
565 if (!driveCheck)
566 throw Utf8StrFmt(Storage::tr("The given attachment is not supported by the storage controller '%s'"), pszCtl);
567 }
568 else
569 goto leave;
570 }
571
572 // find the medium given
573 /* host drive? */
574 if (!RTStrNICmp(pszMedium, RT_STR_TUPLE("host:")))
575 {
576 ComPtr<IHost> host;
577 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
578
579 if (devTypeRequested == DeviceType_DVD)
580 {
581 hrc = host->FindHostDVDDrive(Bstr(pszMedium + 5).raw(),
582 pMedium2Mount.asOutParam());
583 if (!pMedium2Mount)
584 {
585 /* 2nd try: try with the real name, important on Linux+libhal */
586 char szPathReal[RTPATH_MAX];
587 if (RT_FAILURE(RTPathReal(pszMedium + 5, szPathReal, sizeof(szPathReal))))
588 throw Utf8StrFmt(Storage::tr("Invalid host DVD drive name \"%s\""), pszMedium + 5);
589 hrc = host->FindHostDVDDrive(Bstr(szPathReal).raw(),
590 pMedium2Mount.asOutParam());
591 if (!pMedium2Mount)
592 throw Utf8StrFmt(Storage::tr("Invalid host DVD drive name \"%s\""), pszMedium + 5);
593 }
594 }
595 else
596 {
597 // floppy
598 hrc = host->FindHostFloppyDrive(Bstr(pszMedium + 5).raw(),
599 pMedium2Mount.asOutParam());
600 if (!pMedium2Mount)
601 throw Utf8StrFmt(Storage::tr("Invalid host floppy drive name \"%s\""), pszMedium + 5);
602 }
603 }
604 else if (!RTStrICmp(pszMedium, "iSCSI"))
605 {
606 /* check for required options */
607 if (bstrServer.isEmpty() || bstrTarget.isEmpty())
608 throw Utf8StrFmt(Storage::tr("Parameters --server and --target are required for iSCSI media"));
609
610 /** @todo move the location stuff to Main, which can use pfnComposeName
611 * from the disk backends to construct the location properly. Also do
612 * not use slashes to separate the parts, as otherwise only the last
613 * element containing information will be shown. */
614 Bstr bstrISCSIMedium;
615 if ( bstrLun.isEmpty()
616 || (bstrLun == "0")
617 || (bstrLun == "enc0")
618 )
619 bstrISCSIMedium = BstrFmt("%ls|%ls", bstrServer.raw(), bstrTarget.raw());
620 else
621 bstrISCSIMedium = BstrFmt("%ls|%ls|%ls", bstrServer.raw(), bstrTarget.raw(), bstrLun.raw());
622
623 CHECK_ERROR(a->virtualBox, CreateMedium(Bstr("iSCSI").raw(),
624 bstrISCSIMedium.raw(),
625 AccessMode_ReadWrite,
626 DeviceType_HardDisk,
627 pMedium2Mount.asOutParam()));
628 if (FAILED(hrc)) goto leave; /** @todo r=andy Argh!! Why not using exceptions here? */
629 if (!bstrPort.isEmpty())
630 bstrServer = BstrFmt("%ls:%ls", bstrServer.raw(), bstrPort.raw());
631
632 // set the other iSCSI parameters as properties
633 com::SafeArray <BSTR> names;
634 com::SafeArray <BSTR> values;
635 Bstr("TargetAddress").detachTo(names.appendedRaw());
636 bstrServer.detachTo(values.appendedRaw());
637 Bstr("TargetName").detachTo(names.appendedRaw());
638 bstrTarget.detachTo(values.appendedRaw());
639
640 if (!bstrLun.isEmpty())
641 {
642 Bstr("LUN").detachTo(names.appendedRaw());
643 bstrLun.detachTo(values.appendedRaw());
644 }
645 if (!bstrUsername.isEmpty())
646 {
647 Bstr("InitiatorUsername").detachTo(names.appendedRaw());
648 bstrUsername.detachTo(values.appendedRaw());
649 }
650 if (!bstrPassword.isEmpty())
651 {
652 Bstr("InitiatorSecret").detachTo(names.appendedRaw());
653 bstrPassword.detachTo(values.appendedRaw());
654 }
655 if (!bstrInitiator.isEmpty())
656 {
657 Bstr("InitiatorName").detachTo(names.appendedRaw());
658 bstrInitiator.detachTo(values.appendedRaw());
659 }
660
661 /// @todo add --targetName and --targetPassword options
662
663 if (fIntNet)
664 {
665 Bstr("HostIPStack").detachTo(names.appendedRaw());
666 Bstr("0").detachTo(values.appendedRaw());
667 }
668
669 CHECK_ERROR(pMedium2Mount, SetProperties(ComSafeArrayAsInParam(names),
670 ComSafeArrayAsInParam(values)));
671 if (FAILED(hrc)) goto leave;
672 Bstr guid;
673 CHECK_ERROR(pMedium2Mount, COMGETTER(Id)(guid.asOutParam()));
674 if (FAILED(hrc)) goto leave;
675 RTPrintf(Storage::tr("iSCSI disk created. UUID: %s\n"), Utf8Str(guid).c_str());
676 }
677 else
678 {
679 if (!pszMedium)
680 {
681 ComPtr<IMediumAttachment> mediumAttachment;
682 hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port,
683 device,
684 mediumAttachment.asOutParam());
685 if (FAILED(hrc))
686 throw Utf8Str(Storage::tr("Missing --medium argument"));
687 }
688 else
689 {
690 Bstr bstrMedium(pszMedium);
691 hrc = openMedium(a, pszMedium, devTypeRequested,
692 AccessMode_ReadWrite, pMedium2Mount,
693 fSetNewUuid, false /* fSilent */);
694 if (FAILED(hrc) || !pMedium2Mount)
695 throw Utf8StrFmt(Storage::tr("Invalid UUID or filename \"%s\""), pszMedium);
696 }
697 }
698
699 // set medium/parent medium UUID, if so desired
700 if (pMedium2Mount && (fSetNewUuid || fSetNewParentUuid))
701 {
702 CHECK_ERROR(pMedium2Mount, SetIds(fSetNewUuid, bstrNewUuid.raw(),
703 fSetNewParentUuid, bstrNewParentUuid.raw()));
704 if (FAILED(hrc))
705 throw Utf8Str(Storage::tr("Failed to set the medium/parent medium UUID"));
706 }
707
708 // set medium type, if so desired
709 if (pMedium2Mount && fSetMediumType)
710 {
711 MediumType_T enmMediumTypeOld;
712 CHECK_ERROR(pMedium2Mount, COMGETTER(Type)(&enmMediumTypeOld));
713 if (SUCCEEDED(hrc))
714 {
715 if (enmMediumTypeOld != enmMediumType)
716 {
717 CHECK_ERROR(pMedium2Mount, COMSETTER(Type)(enmMediumType));
718 if (FAILED(hrc))
719 throw Utf8Str(Storage::tr("Failed to set the medium type"));
720 }
721 }
722 }
723
724 if (pMedium2Mount && !bstrComment.isEmpty())
725 {
726 CHECK_ERROR(pMedium2Mount, COMSETTER(Description)(bstrComment.raw()));
727 }
728
729 if (pszMedium)
730 {
731 switch (devTypeRequested)
732 {
733 case DeviceType_DVD:
734 case DeviceType_Floppy:
735 {
736 if (!fRunTime)
737 {
738 ComPtr<IMediumAttachment> mediumAttachment;
739 // check if there is a dvd/floppy drive at the given location, if not attach one first
740 hrc = machine->GetMediumAttachment(Bstr(pszCtl).raw(),
741 port,
742 device,
743 mediumAttachment.asOutParam());
744 if (SUCCEEDED(hrc))
745 {
746 DeviceType_T deviceType;
747 mediumAttachment->COMGETTER(Type)(&deviceType);
748 if (deviceType != devTypeRequested)
749 {
750 machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
751 hrc = machine->AttachDeviceWithoutMedium(Bstr(pszCtl).raw(),
752 port,
753 device,
754 devTypeRequested); // DeviceType_DVD or DeviceType_Floppy
755 }
756 }
757 else
758 {
759 hrc = machine->AttachDeviceWithoutMedium(Bstr(pszCtl).raw(),
760 port,
761 device,
762 devTypeRequested); // DeviceType_DVD or DeviceType_Floppy
763 }
764 }
765
766 if (pMedium2Mount)
767 {
768 CHECK_ERROR(machine, MountMedium(Bstr(pszCtl).raw(),
769 port,
770 device,
771 pMedium2Mount,
772 fForceUnmount));
773 }
774 break;
775 } // end DeviceType_DVD or DeviceType_Floppy:
776
777 case DeviceType_HardDisk:
778 {
779 // if there is anything attached at the given location, remove it
780 machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
781 CHECK_ERROR(machine, AttachDevice(Bstr(pszCtl).raw(),
782 port,
783 device,
784 DeviceType_HardDisk,
785 pMedium2Mount));
786 break;
787 }
788
789 default: break; /* Shut up MSC */
790 }
791 }
792 }
793
794 if ( pszPassThrough
795 && (SUCCEEDED(hrc)))
796 {
797 ComPtr<IMediumAttachment> mattach;
798 CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
799 device, mattach.asOutParam()));
800
801 if (SUCCEEDED(hrc))
802 {
803 if (!RTStrICmp(pszPassThrough, "on"))
804 {
805 CHECK_ERROR(machine, PassthroughDevice(Bstr(pszCtl).raw(),
806 port, device, TRUE));
807 }
808 else if (!RTStrICmp(pszPassThrough, "off"))
809 {
810 CHECK_ERROR(machine, PassthroughDevice(Bstr(pszCtl).raw(),
811 port, device, FALSE));
812 }
813 else
814 throw Utf8StrFmt(Storage::tr("Invalid --passthrough argument '%s'"), pszPassThrough);
815 }
816 else
817 throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl);
818 }
819
820 if ( pszTempEject
821 && (SUCCEEDED(hrc)))
822 {
823 ComPtr<IMediumAttachment> mattach;
824 CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
825 device, mattach.asOutParam()));
826
827 if (SUCCEEDED(hrc))
828 {
829 if (!RTStrICmp(pszTempEject, "on"))
830 {
831 CHECK_ERROR(machine, TemporaryEjectDevice(Bstr(pszCtl).raw(),
832 port, device, TRUE));
833 }
834 else if (!RTStrICmp(pszTempEject, "off"))
835 {
836 CHECK_ERROR(machine, TemporaryEjectDevice(Bstr(pszCtl).raw(),
837 port, device, FALSE));
838 }
839 else
840 throw Utf8StrFmt(Storage::tr("Invalid --tempeject argument '%s'"), pszTempEject);
841 }
842 else
843 throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl);
844 }
845
846 if ( pszNonRotational
847 && (SUCCEEDED(hrc)))
848 {
849 ComPtr<IMediumAttachment> mattach;
850 CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
851 device, mattach.asOutParam()));
852
853 if (SUCCEEDED(hrc))
854 {
855 if (!RTStrICmp(pszNonRotational, "on"))
856 {
857 CHECK_ERROR(machine, NonRotationalDevice(Bstr(pszCtl).raw(),
858 port, device, TRUE));
859 }
860 else if (!RTStrICmp(pszNonRotational, "off"))
861 {
862 CHECK_ERROR(machine, NonRotationalDevice(Bstr(pszCtl).raw(),
863 port, device, FALSE));
864 }
865 else
866 throw Utf8StrFmt(Storage::tr("Invalid --nonrotational argument '%s'"), pszNonRotational);
867 }
868 else
869 throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl);
870 }
871
872 if ( pszDiscard
873 && (SUCCEEDED(hrc)))
874 {
875 ComPtr<IMediumAttachment> mattach;
876 CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
877 device, mattach.asOutParam()));
878
879 if (SUCCEEDED(hrc))
880 {
881 if (!RTStrICmp(pszDiscard, "on"))
882 {
883 CHECK_ERROR(machine, SetAutoDiscardForDevice(Bstr(pszCtl).raw(),
884 port, device, TRUE));
885 }
886 else if (!RTStrICmp(pszDiscard, "off"))
887 {
888 CHECK_ERROR(machine, SetAutoDiscardForDevice(Bstr(pszCtl).raw(),
889 port, device, FALSE));
890 }
891 else
892 throw Utf8StrFmt(Storage::tr("Invalid --discard argument '%s'"), pszDiscard);
893 }
894 else
895 throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl);
896 }
897
898 if ( pszHotPluggable
899 && (SUCCEEDED(hrc)))
900 {
901 ComPtr<IMediumAttachment> mattach;
902 CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
903 device, mattach.asOutParam()));
904
905 if (SUCCEEDED(hrc))
906 {
907 if (!RTStrICmp(pszHotPluggable, "on"))
908 {
909 CHECK_ERROR(machine, SetHotPluggableForDevice(Bstr(pszCtl).raw(),
910 port, device, TRUE));
911 }
912 else if (!RTStrICmp(pszHotPluggable, "off"))
913 {
914 CHECK_ERROR(machine, SetHotPluggableForDevice(Bstr(pszCtl).raw(),
915 port, device, FALSE));
916 }
917 else
918 throw Utf8StrFmt(Storage::tr("Invalid --hotpluggable argument '%s'"), pszHotPluggable);
919 }
920 else
921 throw Utf8StrFmt(Storage::tr("Couldn't find the controller attachment for the controller '%s'\n"), pszCtl);
922 }
923
924 if ( pszBandwidthGroup
925 && !fRunTime
926 && SUCCEEDED(hrc))
927 {
928
929 if (!RTStrICmp(pszBandwidthGroup, "none"))
930 {
931 /* Just remove the bandwidth gorup. */
932 CHECK_ERROR(machine, SetNoBandwidthGroupForDevice(Bstr(pszCtl).raw(),
933 port, device));
934 }
935 else
936 {
937 ComPtr<IBandwidthControl> bwCtrl;
938 ComPtr<IBandwidthGroup> bwGroup;
939
940 CHECK_ERROR(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()));
941
942 if (SUCCEEDED(hrc))
943 {
944 CHECK_ERROR(bwCtrl, GetBandwidthGroup(Bstr(pszBandwidthGroup).raw(), bwGroup.asOutParam()));
945 if (SUCCEEDED(hrc))
946 {
947 CHECK_ERROR(machine, SetBandwidthGroupForDevice(Bstr(pszCtl).raw(),
948 port, device, bwGroup));
949 }
950 }
951 }
952 }
953
954 /* commit changes */
955 if (SUCCEEDED(hrc))
956 CHECK_ERROR(machine, SaveSettings());
957 }
958 catch (const Utf8Str &strError)
959 {
960 errorArgument("%s", strError.c_str());
961 hrc = E_FAIL;
962 }
963
964 // machine must always be unlocked, even on errors
965leave:
966 a->session->UnlockMachine();
967
968 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
969}
970
971
972static const RTGETOPTDEF g_aStorageControllerOptions[] =
973{
974 { "--name", 'n', RTGETOPT_REQ_STRING },
975 { "--add", 'a', RTGETOPT_REQ_STRING },
976 { "--controller", 'c', RTGETOPT_REQ_STRING },
977 { "--portcount", 'p', RTGETOPT_REQ_UINT32 },
978 { "--remove", 'r', RTGETOPT_REQ_NOTHING },
979 { "--rename", 'R', RTGETOPT_REQ_STRING },
980 { "--hostiocache", 'i', RTGETOPT_REQ_STRING },
981 { "--bootable", 'b', RTGETOPT_REQ_STRING },
982};
983
984RTEXITCODE handleStorageController(HandlerArg *a)
985{
986 int c;
987 const char *pszCtl = NULL;
988 const char *pszBusType = NULL;
989 const char *pszCtlType = NULL;
990 const char *pszHostIOCache = NULL;
991 const char *pszBootable = NULL;
992 const char *pszCtlNewName = NULL;
993 ULONG portcount = ~0U;
994 bool fRemoveCtl = false;
995 ComPtr<IMachine> machine;
996 RTGETOPTUNION ValueUnion;
997 RTGETOPTSTATE GetState;
998
999 if (a->argc < 4)
1000 return errorSyntax(Storage::tr("Too few parameters"));
1001
1002 RTGetOptInit (&GetState, a->argc, a->argv, g_aStorageControllerOptions,
1003 RT_ELEMENTS(g_aStorageControllerOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1004
1005 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1006 {
1007 switch (c)
1008 {
1009 case 'n': // controller name
1010 Assert(ValueUnion.psz);
1011 pszCtl = ValueUnion.psz;
1012 break;
1013
1014 case 'a': // controller bus type <ide/sata/scsi/floppy>
1015 Assert(ValueUnion.psz);
1016 pszBusType = ValueUnion.psz;
1017 break;
1018
1019 case 'c': // controller <lsilogic/buslogic/intelahci/piix3/piix4/ich6/i82078>
1020 Assert(ValueUnion.psz);
1021 pszCtlType = ValueUnion.psz;
1022 break;
1023
1024 case 'p': // portcount
1025 portcount = ValueUnion.u32;
1026 break;
1027
1028 case 'r': // remove controller
1029 fRemoveCtl = true;
1030 break;
1031
1032 case 'R': // rename controller
1033 Assert(ValueUnion.psz);
1034 pszCtlNewName = ValueUnion.psz;
1035 break;
1036
1037 case 'i':
1038 pszHostIOCache = ValueUnion.psz;
1039 break;
1040
1041 case 'b':
1042 pszBootable = ValueUnion.psz;
1043 break;
1044
1045 default:
1046 return errorGetOpt(c, &ValueUnion);
1047 }
1048 }
1049
1050 HRESULT hrc;
1051
1052 /* try to find the given machine */
1053 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1054 machine.asOutParam()), RTEXITCODE_FAILURE);
1055
1056 /* open a session for the VM */
1057 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1058
1059 /* get the mutable session machine */
1060 a->session->COMGETTER(Machine)(machine.asOutParam());
1061
1062 if (!pszCtl)
1063 {
1064 /* it's important to always close sessions */
1065 a->session->UnlockMachine();
1066 return errorSyntax(Storage::tr("Storage controller name not specified\n"));
1067 }
1068
1069 if (fRemoveCtl)
1070 {
1071 CHECK_ERROR(machine, RemoveStorageController(Bstr(pszCtl).raw()));
1072 }
1073 else
1074 {
1075 if (pszBusType)
1076 {
1077 ComPtr<IStorageController> ctl;
1078
1079 if (!RTStrICmp(pszBusType, "ide"))
1080 {
1081 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1082 StorageBus_IDE,
1083 ctl.asOutParam()));
1084 }
1085 else if (!RTStrICmp(pszBusType, "sata"))
1086 {
1087 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1088 StorageBus_SATA,
1089 ctl.asOutParam()));
1090 }
1091 else if (!RTStrICmp(pszBusType, "scsi"))
1092 {
1093 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1094 StorageBus_SCSI,
1095 ctl.asOutParam()));
1096 }
1097 else if (!RTStrICmp(pszBusType, "floppy"))
1098 {
1099 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1100 StorageBus_Floppy,
1101 ctl.asOutParam()));
1102 }
1103 else if (!RTStrICmp(pszBusType, "sas"))
1104 {
1105 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1106 StorageBus_SAS,
1107 ctl.asOutParam()));
1108 }
1109 else if (!RTStrICmp(pszBusType, "usb"))
1110 {
1111 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1112 StorageBus_USB,
1113 ctl.asOutParam()));
1114 }
1115 else if (!RTStrICmp(pszBusType, "pcie"))
1116 {
1117 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1118 StorageBus_PCIe,
1119 ctl.asOutParam()));
1120 }
1121 else if (!RTStrICmp(pszBusType, "virtio-scsi") || !RTStrICmp(pszBusType, "virtio"))
1122 {
1123 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1124 StorageBus_VirtioSCSI,
1125 ctl.asOutParam()));
1126 }
1127 else
1128 {
1129 errorArgument(Storage::tr("Invalid --add argument '%s'"), pszBusType);
1130 hrc = E_FAIL;
1131 }
1132 }
1133
1134 if ( pszCtlType
1135 && SUCCEEDED(hrc))
1136 {
1137 ComPtr<IStorageController> ctl;
1138
1139 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1140 ctl.asOutParam()));
1141
1142 if (SUCCEEDED(hrc))
1143 {
1144 if (!RTStrICmp(pszCtlType, "lsilogic"))
1145 {
1146 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogic));
1147 }
1148 else if (!RTStrICmp(pszCtlType, "buslogic"))
1149 {
1150 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic));
1151 }
1152 else if (!RTStrICmp(pszCtlType, "intelahci"))
1153 {
1154 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_IntelAhci));
1155 }
1156 else if (!RTStrICmp(pszCtlType, "piix3"))
1157 {
1158 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX3));
1159 }
1160 else if (!RTStrICmp(pszCtlType, "piix4"))
1161 {
1162 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX4));
1163 }
1164 else if (!RTStrICmp(pszCtlType, "ich6"))
1165 {
1166 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_ICH6));
1167 }
1168 else if (!RTStrICmp(pszCtlType, "i82078"))
1169 {
1170 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_I82078));
1171 }
1172 else if (!RTStrICmp(pszCtlType, "lsilogicsas"))
1173 {
1174 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas));
1175 }
1176 else if (!RTStrICmp(pszCtlType, "usb"))
1177 {
1178 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_USB));
1179 }
1180 else if (!RTStrICmp(pszCtlType, "nvme"))
1181 {
1182 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_NVMe));
1183 }
1184 else if (!RTStrICmp(pszCtlType, "virtio-scsi") || !RTStrICmp(pszCtlType, "virtio"))
1185 {
1186 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_VirtioSCSI));
1187 }
1188 else
1189 {
1190 errorArgument(Storage::tr("Invalid --type argument '%s'"), pszCtlType);
1191 hrc = E_FAIL;
1192 }
1193 }
1194 else
1195 {
1196 errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl);
1197 hrc = E_FAIL;
1198 }
1199 }
1200
1201 if ( (portcount != ~0U)
1202 && SUCCEEDED(hrc))
1203 {
1204 ComPtr<IStorageController> ctl;
1205
1206 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1207 ctl.asOutParam()));
1208
1209 if (SUCCEEDED(hrc))
1210 {
1211 CHECK_ERROR(ctl, COMSETTER(PortCount)(portcount));
1212 }
1213 else
1214 {
1215 errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl);
1216 hrc = E_FAIL; /** @todo r=andy Overwrites original hrc. */
1217 }
1218 }
1219
1220 if ( pszHostIOCache
1221 && SUCCEEDED(hrc))
1222 {
1223 ComPtr<IStorageController> ctl;
1224
1225 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1226 ctl.asOutParam()));
1227
1228 if (SUCCEEDED(hrc))
1229 {
1230 if (!RTStrICmp(pszHostIOCache, "on"))
1231 {
1232 CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(TRUE));
1233 }
1234 else if (!RTStrICmp(pszHostIOCache, "off"))
1235 {
1236 CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(FALSE));
1237 }
1238 else
1239 {
1240 errorArgument(Storage::tr("Invalid --hostiocache argument '%s'"), pszHostIOCache);
1241 hrc = E_FAIL;
1242 }
1243 }
1244 else
1245 {
1246 errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl);
1247 hrc = E_FAIL; /** @todo r=andy Ditto. */
1248 }
1249 }
1250
1251 if ( pszBootable
1252 && SUCCEEDED(hrc))
1253 {
1254 if (SUCCEEDED(hrc))
1255 {
1256 if (!RTStrICmp(pszBootable, "on"))
1257 {
1258 CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), TRUE));
1259 }
1260 else if (!RTStrICmp(pszBootable, "off"))
1261 {
1262 CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), FALSE));
1263 }
1264 else
1265 {
1266 errorArgument(Storage::tr("Invalid --bootable argument '%s'"), pszBootable);
1267 hrc = E_FAIL;
1268 }
1269 }
1270 else
1271 {
1272 errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl);
1273 hrc = E_FAIL;
1274 }
1275 }
1276
1277 if ( pszCtlNewName
1278 && SUCCEEDED(hrc))
1279 {
1280 ComPtr<IStorageController> ctl;
1281
1282 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1283 ctl.asOutParam()));
1284
1285 if (SUCCEEDED(hrc))
1286 {
1287 CHECK_ERROR(ctl, COMSETTER(Name)(Bstr(pszCtlNewName).raw()));
1288 }
1289 else
1290 {
1291 errorArgument(Storage::tr("Couldn't find the controller with the name: '%s'\n"), pszCtl);
1292 hrc = E_FAIL;
1293 }
1294 }
1295
1296 }
1297
1298 /* commit changes */
1299 if (SUCCEEDED(hrc))
1300 CHECK_ERROR(machine, SaveSettings());
1301
1302 /* it's important to always close sessions */
1303 a->session->UnlockMachine();
1304
1305 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1306}
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