VirtualBox

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

Last change on this file since 67816 was 63300, checked in by vboxsync, 8 years ago

VBoxManage: warnings

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