VirtualBox

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

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