VirtualBox

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

Last change on this file since 55263 was 54267, checked in by vboxsync, 10 years ago

Frontends/VBoxManage: collect DHCP options specified on the command
line into a vector instead of a map so that we pass all duplicates to
the API.

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