VirtualBox

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

Last change on this file since 56339 was 56118, checked in by vboxsync, 10 years ago

VBoxManage: A quick command handler return-code cleanup that turned out to be rather tedious.

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