VirtualBox

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

Last change on this file since 40673 was 38887, checked in by vboxsync, 13 years ago

Change DiscardDevice to SetAutoDiscardForDevice

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