VirtualBox

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

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

Frontends/VBoxManage: Allow adding virtio-net storage controller.

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