VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp@ 78296

Last change on this file since 78296 was 77595, checked in by vboxsync, 5 years ago

VBoxManage: Re-do help category handling (command/sub-command specific help) for old-style commands to lift the low limit of the bit field approach for everything. This is now much closer to the handling of new-style commands, but far from converting anything old. While at it I eliminated the need to define an entry in the (old-style) USAGECATEGORY enum for new-style commands.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.1 KB
RevLine 
[18108]1/* $Id: VBoxManageDHCPServer.cpp 77595 2019-03-07 12:42:31Z vboxsync $ */
[17892]2/** @file
[18072]3 * VBoxManage - Implementation of dhcpserver command.
[17892]4 */
5
6/*
[76553]7 * Copyright (C) 2006-2019 Oracle Corporation
[17892]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
[57358]18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
[17892]22#ifndef VBOX_ONLY_DOCS
23#include <VBox/com/com.h>
24#include <VBox/com/array.h>
25#include <VBox/com/ErrorInfo.h>
[20928]26#include <VBox/com/errorprint.h>
[17892]27#include <VBox/com/VirtualBox.h>
28#endif /* !VBOX_ONLY_DOCS */
29
30#include <iprt/cidr.h>
31#include <iprt/param.h>
32#include <iprt/path.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/net.h>
36#include <iprt/getopt.h>
[18108]37#include <iprt/ctype.h>
[17892]38
39#include <VBox/log.h>
40
41#include "VBoxManage.h"
42
[47021]43#include <string>
44#include <vector>
45#include <map>
46
[17892]47#ifndef VBOX_ONLY_DOCS
48using namespace com;
49
50typedef enum enMainOpCodes
51{
52 OP_ADD = 1000,
53 OP_REMOVE,
[75648]54 OP_MODIFY,
55 OP_RESTART
[17892]56} OPCODE;
57
[54267]58typedef std::pair<DhcpOpt_T, std::string> DhcpOptSpec;
59typedef std::vector<DhcpOptSpec> DhcpOpts;
60typedef DhcpOpts::iterator DhcpOptIterator;
[47021]61
[75819]62typedef std::vector<DhcpOpt_T> DhcpOptIds;
63typedef DhcpOptIds::iterator DhcpOptIdIterator;
64
[47021]65struct VmNameSlotKey
66{
[54263]67 const std::string VmName;
[47021]68 uint8_t u8Slot;
69
[63300]70 VmNameSlotKey(const std::string &aVmName, uint8_t aSlot)
71 : VmName(aVmName)
72 , u8Slot(aSlot)
73 {}
[47021]74
75 bool operator< (const VmNameSlotKey& that) const
76 {
77 if (VmName == that.VmName)
78 return u8Slot < that.u8Slot;
79 else
80 return VmName < that.VmName;
81 }
82};
83
[54267]84typedef std::map<VmNameSlotKey, DhcpOpts> VmSlot2OptionsM;
[47021]85typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator;
86typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair;
87
[75819]88typedef std::map<VmNameSlotKey, DhcpOptIds> VmSlot2OptionIdsM;
89typedef VmSlot2OptionIdsM::iterator VmSlot2OptionIdsIterator;
90
[47021]91typedef std::vector<VmNameSlotKey> VmConfigs;
92
[63300]93static const RTGETOPTDEF g_aDHCPIPOptions[] =
94{
95 { "--netname", 't', RTGETOPT_REQ_STRING }, /* we use 't' instead of 'n' to avoid
96 * 1. the misspelled "-enable" long option to be treated as 'e' (for -enable) + 'n' (for -netname) + "<the_rest_opt>" (for net name)
97 * 2. the misspelled "-netmask" to be treated as 'n' (for -netname) + "<the_rest_opt>" (for net name)
98 */
99 { "-netname", 't', RTGETOPT_REQ_STRING }, // deprecated (if removed check below)
100 { "--ifname", 'f', RTGETOPT_REQ_STRING }, /* we use 'f' instead of 'i' to avoid
101 * 1. the misspelled "-disable" long option to be treated as 'd' (for -disable) + 'i' (for -ifname) + "<the_rest_opt>" (for if name)
102 */
103 { "-ifname", 'f', RTGETOPT_REQ_STRING }, // deprecated
104 { "--ip", 'a', RTGETOPT_REQ_STRING },
105 { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
106 { "--netmask", 'm', RTGETOPT_REQ_STRING },
107 { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
108 { "--lowerip", 'l', RTGETOPT_REQ_STRING },
109 { "-lowerip", 'l', RTGETOPT_REQ_STRING }, // deprecated
110 { "--upperip", 'u', RTGETOPT_REQ_STRING },
111 { "-upperip", 'u', RTGETOPT_REQ_STRING }, // deprecated
112 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
113 { "-enable", 'e', RTGETOPT_REQ_NOTHING }, // deprecated
114 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
115 { "-disable", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
116 { "--options", 'o', RTGETOPT_REQ_NOTHING },
[75945]117 { "--vm", 'M', RTGETOPT_REQ_STRING}, /* only with -o */
118 { "--nic", 'n', RTGETOPT_REQ_UINT8}, /* only with -o and -M */
[63300]119 { "--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */
[75819]120 { "--value", 'p', RTGETOPT_REQ_STRING}, /* only with -i */
121 { "--remove", 'r', RTGETOPT_REQ_NOTHING} /* only with -i */
[63300]122};
[47021]123
[63300]124static RTEXITCODE handleOp(HandlerArg *a, OPCODE enmCode, int iStart)
[17892]125{
126 if (a->argc - iStart < 2)
127 return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
128
129 int index = iStart;
130 HRESULT rc;
[47021]131 bool fOptionsRead = false;
132 bool fVmOptionRead = false;
[17892]133
[47021]134 const char *pszVmName = NULL;
[17892]135 const char *pNetName = NULL;
136 const char *pIfName = NULL;
137 const char * pIp = NULL;
138 const char * pNetmask = NULL;
139 const char * pLowerIp = NULL;
140 const char * pUpperIp = NULL;
[47021]141
[48776]142 uint8_t u8OptId = (uint8_t)~0;
143 uint8_t u8Slot = (uint8_t)~0;
[47021]144
[17892]145 int enable = -1;
146
[75819]147 DhcpOpts GlobalDhcpOptions;
148 DhcpOptIds GlobalDhcpOptions2Delete;
149 VmSlot2OptionsM VmSlot2Options;
150 VmSlot2OptionIdsM VmSlot2Options2Delete;
151 VmConfigs VmConfigs2Delete;
[47021]152
[17892]153 int c;
154 RTGETOPTUNION ValueUnion;
155 RTGETOPTSTATE GetState;
156 RTGetOptInit(&GetState,
157 a->argc,
158 a->argv,
[18108]159 g_aDHCPIPOptions,
[26517]160 enmCode != OP_REMOVE ? RT_ELEMENTS(g_aDHCPIPOptions) : 4, /* we use only --netname and --ifname for remove*/
[17892]161 index,
[26517]162 RTGETOPTINIT_FLAGS_NO_STD_OPTS);
[17892]163 while ((c = RTGetOpt(&GetState, &ValueUnion)))
164 {
165 switch (c)
166 {
[18323]167 case 't': // --netname
[17892]168 if(pNetName)
[18108]169 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --netname once.");
[17892]170 else if (pIfName)
[30322]171 return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ifname for identifying the DHCP server.");
[17892]172 else
173 {
174 pNetName = ValueUnion.psz;
175 }
176 break;
[18323]177 case 'f': // --ifname
[17892]178 if(pIfName)
[18108]179 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ifname once.");
[17892]180 else if (pNetName)
[30322]181 return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ipname for identifying the DHCP server.");
[17892]182 else
183 {
184 pIfName = ValueUnion.psz;
185 }
186 break;
[18108]187 case 'a': // -ip
[17892]188 if(pIp)
[18108]189 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ip once.");
[17892]190 else
191 {
192 pIp = ValueUnion.psz;
193 }
194 break;
[18108]195 case 'm': // --netmask
[17892]196 if(pNetmask)
[18108]197 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --netmask once.");
[17892]198 else
199 {
200 pNetmask = ValueUnion.psz;
201 }
202 break;
[18108]203 case 'l': // --lowerip
[17892]204 if(pLowerIp)
[18108]205 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --lowerip once.");
[17892]206 else
207 {
208 pLowerIp = ValueUnion.psz;
209 }
210 break;
[18108]211 case 'u': // --upperip
[17892]212 if(pUpperIp)
[18108]213 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --upperip once.");
[17892]214 else
215 {
216 pUpperIp = ValueUnion.psz;
217 }
218 break;
[18108]219 case 'e': // --enable
[17892]220 if(enable >= 0)
[18108]221 return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
[17892]222 else
223 {
224 enable = 1;
225 }
226 break;
[18108]227 case 'd': // --disable
[17892]228 if(enable >= 0)
[18108]229 return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
[17892]230 else
231 {
232 enable = 0;
233 }
234 break;
235 case VINF_GETOPT_NOT_OPTION:
[18108]236 return errorSyntax(USAGE_DHCPSERVER, "unhandled parameter: %s", ValueUnion.psz);
[17892]237 break;
[47021]238
239 case 'o': // --options
240 {
241 // {"--vm", 'n', RTGETOPT_REQ_STRING}, /* only with -o */
[75918]242 // {"--nic", 'c', RTGETOPT_REQ_UINT8}, /* only with -o and -n*/
[47021]243 // {"--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */
244 // {"--value", 'p', RTGETOPT_REQ_STRING} /* only with -i */
245 if (fOptionsRead)
246 return errorSyntax(USAGE_DHCPSERVER,
247 "previos option edition wasn't finished");
248 fOptionsRead = true;
249 fVmOptionRead = false; /* we want specify new global or vm option*/
250 u8Slot = (uint8_t)~0;
251 u8OptId = (uint8_t)~0;
252 pszVmName = NULL;
253 } /* end of --options */
254 break;
255
[75945]256 case 'M': // --vm
[47021]257 {
258 if (fVmOptionRead)
259 return errorSyntax(USAGE_DHCPSERVER,
260 "previous vm option edition wasn't finished");
261 else
262 fVmOptionRead = true;
[77595]263 u8Slot = (uint8_t)~0; /* clear slot */
[47021]264 pszVmName = RTStrDup(ValueUnion.psz);
265 }
[75945]266 break; /* end of --vm */
[47021]267
[75945]268 case 'n': // --nic
[47021]269 {
270 if (!fVmOptionRead)
271 return errorSyntax(USAGE_DHCPSERVER,
272 "vm name wasn't specified");
273
274 u8Slot = ValueUnion.u8;
[75918]275
276 if (u8Slot < 1)
277 return errorSyntax(USAGE_DHCPSERVER,
278 "invalid NIC number: %u", u8Slot);
279 --u8Slot;
[47021]280 }
[75918]281 break; /* end of --nic */
[47021]282
283 case 'i': // --id
284 {
285 if (!fOptionsRead)
286 return errorSyntax(USAGE_DHCPSERVER,
287 "-o wasn't found");
288
289 u8OptId = ValueUnion.u8;
290 }
291 break; /* end of --id */
292
293 case 'p': // --value
294 {
295 if (!fOptionsRead)
296 return errorSyntax(USAGE_DHCPSERVER,
297 "-o wasn't found");
298
299 if (u8OptId == (uint8_t)~0)
300 return errorSyntax(USAGE_DHCPSERVER,
301 "--id wasn't found");
302 if ( fVmOptionRead
303 && u8Slot == (uint8_t)~0)
304 return errorSyntax(USAGE_DHCPSERVER,
[75918]305 "--nic wasn't found");
[47021]306
[54267]307 DhcpOpts &opts = fVmOptionRead ? VmSlot2Options[VmNameSlotKey(pszVmName, u8Slot)]
[47451]308 : GlobalDhcpOptions;
[47021]309 std::string strVal = ValueUnion.psz;
[54267]310 opts.push_back(DhcpOptSpec((DhcpOpt_T)u8OptId, strVal));
[47021]311
312 }
313 break; // --end of value
314
[75819]315 case 'r': /* --remove */
316 {
317 if (!fOptionsRead)
318 return errorSyntax(USAGE_DHCPSERVER,
319 "-o wasn't found");
320
321 if (u8OptId == (uint8_t)~0)
322 return errorSyntax(USAGE_DHCPSERVER,
323 "--id wasn't found");
324 if ( fVmOptionRead
325 && u8Slot == (uint8_t)~0)
326 return errorSyntax(USAGE_DHCPSERVER,
[75918]327 "--nic wasn't found");
[75819]328
329 DhcpOptIds &optIds = fVmOptionRead ? VmSlot2Options2Delete[VmNameSlotKey(pszVmName, u8Slot)]
330 : GlobalDhcpOptions2Delete;
331 optIds.push_back((DhcpOpt_T)u8OptId);
332 }
333 break; /* --end of remove */
334
[17892]335 default:
336 if (c > 0)
[18108]337 {
338 if (RT_C_IS_GRAPH(c))
339 return errorSyntax(USAGE_DHCPSERVER, "unhandled option: -%c", c);
[76433]340 return errorSyntax(USAGE_DHCPSERVER, "unhandled option: %i", c);
[18108]341 }
[76433]342 if (c == VERR_GETOPT_UNKNOWN_OPTION)
[18108]343 return errorSyntax(USAGE_DHCPSERVER, "unknown option: %s", ValueUnion.psz);
[76433]344 if (ValueUnion.pDef)
[17892]345 return errorSyntax(USAGE_DHCPSERVER, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
[76433]346 return errorSyntax(USAGE_DHCPSERVER, "%Rrs", c);
[17892]347 }
348 }
349
350 if(! pNetName && !pIfName)
[30322]351 return errorSyntax(USAGE_DHCPSERVER, "You need to specify either --netname or --ifname to identify the DHCP server");
[17892]352
[47021]353 if( enmCode != OP_REMOVE
[75648]354 && enmCode != OP_RESTART
[72826]355 && GlobalDhcpOptions.empty()
[75819]356 && VmSlot2Options.empty()
357 && GlobalDhcpOptions2Delete.empty()
358 && VmSlot2Options2Delete.empty())
[17892]359 {
360 if(enable < 0 || pIp || pNetmask || pLowerIp || pUpperIp)
361 {
362 if(!pIp)
[18108]363 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --ip option");
[17892]364
365 if(!pNetmask)
[18108]366 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --netmask option");
[17892]367
368 if(!pLowerIp)
[18108]369 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --lowerip option");
[17892]370
371 if(!pUpperIp)
[18108]372 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --upperip option");
[17892]373 }
374 }
375
376 Bstr NetName;
377 if(!pNetName)
378 {
379 ComPtr<IHost> host;
380 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
381
382 ComPtr<IHostNetworkInterface> hif;
[26603]383 CHECK_ERROR(host, FindHostNetworkInterfaceByName(Bstr(pIfName).mutableRaw(), hif.asOutParam()));
[30322]384 if (FAILED(rc))
385 return errorArgument("Could not find interface '%s'", pIfName);
[17892]386
387 CHECK_ERROR(hif, COMGETTER(NetworkName) (NetName.asOutParam()));
[30322]388 if (FAILED(rc))
389 return errorArgument("Could not get network name for the interface '%s'", pIfName);
[17892]390 }
391 else
392 {
393 NetName = Bstr(pNetName);
394 }
395
[18023]396 ComPtr<IDHCPServer> svr;
[26603]397 rc = a->virtualBox->FindDHCPServerByNetworkName(NetName.mutableRaw(), svr.asOutParam());
[17892]398 if(enmCode == OP_ADD)
399 {
[30322]400 if (SUCCEEDED(rc))
401 return errorArgument("DHCP server already exists");
[17892]402
[26603]403 CHECK_ERROR(a->virtualBox, CreateDHCPServer(NetName.mutableRaw(), svr.asOutParam()));
[30322]404 if (FAILED(rc))
405 return errorArgument("Failed to create the DHCP server");
[17892]406 }
[30322]407 else if (FAILED(rc))
[17892]408 {
[30322]409 return errorArgument("DHCP server does not exist");
[17892]410 }
411
[75648]412 if (enmCode == OP_RESTART)
[17892]413 {
[75648]414 CHECK_ERROR(svr, Restart());
415 if(FAILED(rc))
416 return errorArgument("Failed to restart server");
417 }
418 else if (enmCode == OP_REMOVE)
419 {
420 CHECK_ERROR(a->virtualBox, RemoveDHCPServer(svr));
421 if(FAILED(rc))
422 return errorArgument("Failed to remove server");
423 }
424 else
425 {
[18011]426 if (pIp || pNetmask || pLowerIp || pUpperIp)
427 {
[47021]428 CHECK_ERROR(svr, SetConfiguration (
429 Bstr(pIp).mutableRaw(),
430 Bstr(pNetmask).mutableRaw(),
431 Bstr(pLowerIp).mutableRaw(),
432 Bstr(pUpperIp).mutableRaw()));
[18011]433 if(FAILED(rc))
[30322]434 return errorArgument("Failed to set configuration");
[18011]435 }
[17892]436
437 if(enable >= 0)
438 {
439 CHECK_ERROR(svr, COMSETTER(Enabled) ((BOOL)enable));
440 }
[47021]441
[75819]442 /* remove specified options */
443 DhcpOptIdIterator itOptId;
444 for (itOptId = GlobalDhcpOptions2Delete.begin();
445 itOptId != GlobalDhcpOptions2Delete.end();
446 ++itOptId)
447 {
448 CHECK_ERROR(svr, RemoveGlobalOption(*itOptId));
449 }
450 VmSlot2OptionIdsIterator itIdVector;
451 for (itIdVector = VmSlot2Options2Delete.begin();
452 itIdVector != VmSlot2Options2Delete.end();
453 ++itIdVector)
454 {
455 for(itOptId = itIdVector->second.begin();
456 itOptId != itIdVector->second.end();
457 ++itOptId)
458 {
459 CHECK_ERROR(svr,
460 RemoveVmSlotOption(Bstr(itIdVector->first.VmName.c_str()).raw(),
461 itIdVector->first.u8Slot,
462 *itOptId));
463 }
464 }
465
[47021]466 /* option processing */
467 DhcpOptIterator itOpt;
468 VmSlot2OptionsIterator it;
469
470 /* Global Options */
471 for(itOpt = GlobalDhcpOptions.begin();
472 itOpt != GlobalDhcpOptions.end();
473 ++itOpt)
474 {
475 CHECK_ERROR(svr,
476 AddGlobalOption(
477 itOpt->first,
478 com::Bstr(itOpt->second.c_str()).raw()));
479 }
480
481 /* heh, vm slot options. */
482
483 for (it = VmSlot2Options.begin();
484 it != VmSlot2Options.end();
485 ++it)
486 {
487 for(itOpt = it->second.begin();
488 itOpt != it->second.end();
489 ++itOpt)
490 {
491 CHECK_ERROR(svr,
492 AddVmSlotOption(Bstr(it->first.VmName.c_str()).raw(),
493 it->first.u8Slot,
494 itOpt->first,
495 com::Bstr(itOpt->second.c_str()).raw()));
496 }
497 }
[17892]498 }
499
[56118]500 return RTEXITCODE_SUCCESS;
[17892]501}
502
503
[56118]504RTEXITCODE handleDHCPServer(HandlerArg *a)
[17892]505{
506 if (a->argc < 1)
507 return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
508
[56118]509 RTEXITCODE rcExit;
[33489]510 if (strcmp(a->argv[0], "modify") == 0)
[63300]511 rcExit = handleOp(a, OP_MODIFY, 1);
[33489]512 else if (strcmp(a->argv[0], "add") == 0)
[63300]513 rcExit = handleOp(a, OP_ADD, 1);
[33489]514 else if (strcmp(a->argv[0], "remove") == 0)
[63300]515 rcExit = handleOp(a, OP_REMOVE, 1);
[75648]516 else if (strcmp(a->argv[0], "restart") == 0)
517 rcExit = handleOp(a, OP_RESTART, 1);
[33489]518 else
[56118]519 rcExit = errorSyntax(USAGE_DHCPSERVER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
[17892]520
[56118]521 return rcExit;
[17892]522}
523
524#endif /* !VBOX_ONLY_DOCS */
[56118]525
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use