VirtualBox

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

Last change on this file since 34913 was 34874, checked in by vboxsync, 14 years ago

Frontends/VBoxManage: fix typo breaking the autodetection of the device type for drives on a non-floppy controller.

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