VirtualBox

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

Last change on this file since 56103 was 54438, checked in by vboxsync, 10 years ago

Main/VirtualBox: eliminate redundant parameter to i_registerMedium, avoiding bugs with registering e.g. floppy images as hard disks.
Main/Medium: switch over to CreateMedium, final API touch-ups, variable name cleanup, typo checking for backend support of fixed image creation, check for backends not supporting a particular device type
Frontends/VBoxManage: cleanup, adapt to new API, generalize all operations which worked on hard disks only to all device types
Frontends/VirtualBox: adapt to new API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.9 KB
Line 
1/* $Id: VBoxManageStorageController.cpp 54438 2015-02-24 11:09:17Z 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
75int 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 1;
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()), 1);
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()), 1);
325 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
326 SessionType_T st;
327 CHECK_ERROR_RET(a->session, COMGETTER(Type)(&st), 1);
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), 1);
350 ULONG maxPorts = 0;
351 CHECK_ERROR_RET(systemProperties, GetMaxPortCountForStorageBus(storageBus, &maxPorts), 1);
352 ULONG maxDevices = 0;
353 CHECK_ERROR_RET(systemProperties, GetMaxDevicesPerPortForStorageBus(storageBus, &maxDevices), 1);
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) ? 0 : 1;
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
953int handleStorageController(HandlerArg *a)
954{
955 int c;
956 HRESULT rc = S_OK;
957 const char *pszCtl = NULL;
958 const char *pszBusType = NULL;
959 const char *pszCtlType = NULL;
960 const char *pszHostIOCache = NULL;
961 const char *pszBootable = NULL;
962 ULONG satabootdev = ~0U;
963 ULONG sataidedev = ~0U;
964 ULONG portcount = ~0U;
965 bool fRemoveCtl = false;
966 ComPtr<IMachine> machine;
967 RTGETOPTUNION ValueUnion;
968 RTGETOPTSTATE GetState;
969
970 if (a->argc < 4)
971 return errorSyntax(USAGE_STORAGECONTROLLER, "Too few parameters");
972
973 RTGetOptInit (&GetState, a->argc, a->argv, g_aStorageControllerOptions,
974 RT_ELEMENTS(g_aStorageControllerOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
975
976 while ( SUCCEEDED(rc)
977 && (c = RTGetOpt(&GetState, &ValueUnion)))
978 {
979 switch (c)
980 {
981 case 'n': // controller name
982 {
983 if (ValueUnion.psz)
984 pszCtl = ValueUnion.psz;
985 else
986 rc = E_FAIL;
987 break;
988 }
989
990 case 'a': // controller bus type <ide/sata/scsi/floppy>
991 {
992 if (ValueUnion.psz)
993 pszBusType = ValueUnion.psz;
994 else
995 rc = E_FAIL;
996 break;
997 }
998
999 case 'c': // controller <lsilogic/buslogic/intelahci/piix3/piix4/ich6/i82078>
1000 {
1001 if (ValueUnion.psz)
1002 pszCtlType = ValueUnion.psz;
1003 else
1004 rc = E_FAIL;
1005 break;
1006 }
1007
1008 case 'p': // portcount
1009 {
1010 portcount = ValueUnion.u32;
1011 break;
1012 }
1013
1014 case 'r': // remove controller
1015 {
1016 fRemoveCtl = true;
1017 break;
1018 }
1019
1020 case 'i':
1021 {
1022 pszHostIOCache = ValueUnion.psz;
1023 break;
1024 }
1025
1026 case 'b':
1027 {
1028 pszBootable = ValueUnion.psz;
1029 break;
1030 }
1031
1032 default:
1033 {
1034 errorGetOpt(USAGE_STORAGECONTROLLER, c, &ValueUnion);
1035 rc = E_FAIL;
1036 break;
1037 }
1038 }
1039 }
1040
1041 if (FAILED(rc))
1042 return 1;
1043
1044 /* try to find the given machine */
1045 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1046 machine.asOutParam()), 1);
1047
1048 /* open a session for the VM */
1049 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), 1);
1050
1051 /* get the mutable session machine */
1052 a->session->COMGETTER(Machine)(machine.asOutParam());
1053
1054 if (!pszCtl)
1055 {
1056 /* it's important to always close sessions */
1057 a->session->UnlockMachine();
1058 errorSyntax(USAGE_STORAGECONTROLLER, "Storage controller name not specified\n");
1059 return 1;
1060 }
1061
1062 if (fRemoveCtl)
1063 {
1064 CHECK_ERROR(machine, RemoveStorageController(Bstr(pszCtl).raw()));
1065 }
1066 else
1067 {
1068 if (pszBusType)
1069 {
1070 ComPtr<IStorageController> ctl;
1071
1072 if (!RTStrICmp(pszBusType, "ide"))
1073 {
1074 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1075 StorageBus_IDE,
1076 ctl.asOutParam()));
1077 }
1078 else if (!RTStrICmp(pszBusType, "sata"))
1079 {
1080 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1081 StorageBus_SATA,
1082 ctl.asOutParam()));
1083 }
1084 else if (!RTStrICmp(pszBusType, "scsi"))
1085 {
1086 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1087 StorageBus_SCSI,
1088 ctl.asOutParam()));
1089 }
1090 else if (!RTStrICmp(pszBusType, "floppy"))
1091 {
1092 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1093 StorageBus_Floppy,
1094 ctl.asOutParam()));
1095 }
1096 else if (!RTStrICmp(pszBusType, "sas"))
1097 {
1098 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1099 StorageBus_SAS,
1100 ctl.asOutParam()));
1101 }
1102 else if (!RTStrICmp(pszBusType, "usb"))
1103 {
1104 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
1105 StorageBus_USB,
1106 ctl.asOutParam()));
1107 }
1108 else
1109 {
1110 errorArgument("Invalid --add argument '%s'", pszBusType);
1111 rc = E_FAIL;
1112 }
1113 }
1114
1115 if ( pszCtlType
1116 && SUCCEEDED(rc))
1117 {
1118 ComPtr<IStorageController> ctl;
1119
1120 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1121 ctl.asOutParam()));
1122
1123 if (SUCCEEDED(rc))
1124 {
1125 if (!RTStrICmp(pszCtlType, "lsilogic"))
1126 {
1127 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogic));
1128 }
1129 else if (!RTStrICmp(pszCtlType, "buslogic"))
1130 {
1131 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic));
1132 }
1133 else if (!RTStrICmp(pszCtlType, "intelahci"))
1134 {
1135 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_IntelAhci));
1136 }
1137 else if (!RTStrICmp(pszCtlType, "piix3"))
1138 {
1139 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX3));
1140 }
1141 else if (!RTStrICmp(pszCtlType, "piix4"))
1142 {
1143 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX4));
1144 }
1145 else if (!RTStrICmp(pszCtlType, "ich6"))
1146 {
1147 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_ICH6));
1148 }
1149 else if (!RTStrICmp(pszCtlType, "i82078"))
1150 {
1151 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_I82078));
1152 }
1153 else if (!RTStrICmp(pszCtlType, "lsilogicsas"))
1154 {
1155 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas));
1156 }
1157 else if (!RTStrICmp(pszCtlType, "usb"))
1158 {
1159 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_USB));
1160 }
1161 else
1162 {
1163 errorArgument("Invalid --type argument '%s'", pszCtlType);
1164 rc = E_FAIL;
1165 }
1166 }
1167 else
1168 {
1169 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1170 rc = E_FAIL;
1171 }
1172 }
1173
1174 if ( (portcount != ~0U)
1175 && SUCCEEDED(rc))
1176 {
1177 ComPtr<IStorageController> ctl;
1178
1179 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1180 ctl.asOutParam()));
1181
1182 if (SUCCEEDED(rc))
1183 {
1184 CHECK_ERROR(ctl, COMSETTER(PortCount)(portcount));
1185 }
1186 else
1187 {
1188 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1189 rc = E_FAIL;
1190 }
1191 }
1192
1193 if ( pszHostIOCache
1194 && SUCCEEDED(rc))
1195 {
1196 ComPtr<IStorageController> ctl;
1197
1198 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1199 ctl.asOutParam()));
1200
1201 if (SUCCEEDED(rc))
1202 {
1203 if (!RTStrICmp(pszHostIOCache, "on"))
1204 {
1205 CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(TRUE));
1206 }
1207 else if (!RTStrICmp(pszHostIOCache, "off"))
1208 {
1209 CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(FALSE));
1210 }
1211 else
1212 {
1213 errorArgument("Invalid --hostiocache argument '%s'", pszHostIOCache);
1214 rc = E_FAIL;
1215 }
1216 }
1217 else
1218 {
1219 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1220 rc = E_FAIL;
1221 }
1222 }
1223
1224 if ( pszBootable
1225 && SUCCEEDED(rc))
1226 {
1227 if (SUCCEEDED(rc))
1228 {
1229 if (!RTStrICmp(pszBootable, "on"))
1230 {
1231 CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), TRUE));
1232 }
1233 else if (!RTStrICmp(pszBootable, "off"))
1234 {
1235 CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), FALSE));
1236 }
1237 else
1238 {
1239 errorArgument("Invalid --bootable argument '%s'", pszBootable);
1240 rc = E_FAIL;
1241 }
1242 }
1243 else
1244 {
1245 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1246 rc = E_FAIL;
1247 }
1248 }
1249 }
1250
1251 /* commit changes */
1252 if (SUCCEEDED(rc))
1253 CHECK_ERROR(machine, SaveSettings());
1254
1255 /* it's important to always close sessions */
1256 a->session->UnlockMachine();
1257
1258 return SUCCEEDED(rc) ? 0 : 1;
1259}
1260
1261#endif /* !VBOX_ONLY_DOCS */
1262
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