1 martin 1.15 //%LICENSE////////////////////////////////////////////////////////////////
|
2 martin 1.16 //
|
3 martin 1.15 // Licensed to The Open Group (TOG) under one or more contributor license
4 // agreements. Refer to the OpenPegasusNOTICE.txt file distributed with
5 // this work for additional information regarding copyright ownership.
6 // Each contributor licenses this file to you under the OpenPegasus Open
7 // Source License; you may not use this file except in compliance with the
8 // License.
|
9 martin 1.16 //
|
10 martin 1.15 // Permission is hereby granted, free of charge, to any person obtaining a
11 // copy of this software and associated documentation files (the "Software"),
12 // to deal in the Software without restriction, including without limitation
13 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 // and/or sell copies of the Software, and to permit persons to whom the
15 // Software is furnished to do so, subject to the following conditions:
|
16 martin 1.16 //
|
17 martin 1.15 // The above copyright notice and this permission notice shall be included
18 // in all copies or substantial portions of the Software.
|
19 martin 1.16 //
|
20 martin 1.15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 martin 1.16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 martin 1.15 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27 martin 1.16 //
|
28 martin 1.15 //////////////////////////////////////////////////////////////////////////
|
29 kumpf 1.2 //
30 //%/////////////////////////////////////////////////////////////////////////////
31
32
33 #include <iostream>
34 #include <Pegasus/Common/Config.h>
35 #include <Pegasus/Common/Constants.h>
36 #include <Pegasus/Common/System.h>
37 #include <Pegasus/Common/FileSystem.h>
38 #include <Pegasus/Common/String.h>
39 #include <Pegasus/Common/PegasusVersion.h>
40 #include <Pegasus/Common/SSLContext.h>
41
42 #include <Pegasus/getoopt/getoopt.h>
43 #include <Clients/cliutils/CommandException.h>
44 #include "IPInfo.h"
45
46 // To build a version of ipinfo that does not
47 // support remote connections the
48 // DISABLE_SUPPORT_FOR_REMOTE_CONNECTIONS
49 // flag can be enabled.
50 kumpf 1.2 //#define DISABLE_SUPPORT_FOR_REMOTE_CONNECTIONS
51
52 //#define DEBUG
53
54 PEGASUS_NAMESPACE_BEGIN
55
56 #define NAMESPACE CIMNamespaceName ("root/cimv2")
57 #define CLASSNAME CIMName ("PG_OperatingSystem")
58
59 /**
60 The command name.
61 */
62 const char IPInfoCommand::COMMAND_NAME [] = "ipinfo";
63
64 /**
65 Label for the usage string for this command.
66 */
67 const char IPInfoCommand::_USAGE [] = "usage: ";
68
69 /**
70 The option character used to specify the hostname.
71 kumpf 1.2 */
72 const char IPInfoCommand::_OPTION_HOSTNAME = 'h';
73
74 /**
75 The option character used to specify the port number.
76 */
77 const char IPInfoCommand::_OPTION_PORTNUMBER = 'p';
78
79 /**
80 The option character used to specify SSL usage.
81 */
82 const char IPInfoCommand::_OPTION_SSL = 's';
83
84 /**
85 The option character used to specify the timeout value.
86 */
87 const char IPInfoCommand::_OPTION_TIMEOUT = 't';
88
89 /**
90 The option character used to specify the username.
91 */
92 kumpf 1.2 const char IPInfoCommand::_OPTION_USERNAME = 'u';
93
94 /**
95 The option character used to specify the password.
96 */
97 const char IPInfoCommand::_OPTION_PASSWORD = 'w';
98
99 /**
100 The option character used to specify debug information.
101 */
102 const char IPInfoCommand::_OPTION_DEBUG = 'd';
103
104 /**
105 The minimum valid portnumber.
106 */
107 const Uint32 IPInfoCommand::_MIN_PORTNUMBER = 0;
108
109 /**
110 The maximum valid portnumber.
111 */
112 const Uint32 IPInfoCommand::_MAX_PORTNUMBER = 65535;
113 kumpf 1.2
114 static const char PASSWORD_PROMPT [] =
|
115 karl 1.9 "Please enter your password: ";
|
116 kumpf 1.2
|
117 kumpf 1.17 static const char PASSWORD_BLANK [] =
|
118 karl 1.9 "Password cannot be blank. Please re-enter your password.";
|
119 kumpf 1.2
120 static const Uint32 MAX_PW_RETRIES = 3;
121
122 static Boolean verifyCertificate(SSLCertificateInfo &certInfo)
123 {
124
125 #ifdef DEBUG
126 cout << certInfo.getSubjectName() << endl;
127 #endif
128 //ATTN-NB-03-05132002: Add code to handle server certificate verification.
129 return true;
130 }
131
132 /**
|
133 kumpf 1.17
|
134 kumpf 1.2 Constructs a IPInfoCommand and initializes instance variables.
|
135 kumpf 1.17
|
136 kumpf 1.2 */
137 IPInfoCommand::IPInfoCommand ()
138 {
139 _hostNameSet = false;
140 _portNumber = WBEM_DEFAULT_HTTP_PORT;
141 _portNumberSet = false;
142
143 char buffer[32];
144 sprintf(buffer, "%lu", (unsigned long) _portNumber);
145 _portNumberStr = buffer;
146
147 _timeout = DEFAULT_TIMEOUT_MILLISECONDS;
148 _userNameSet = false;
149 _passwordSet = false;
150 _useSSL = false;
151 _enableDebug = false;
152
153 String usage = String (_USAGE);
154 usage.append (COMMAND_NAME);
155 usage.append (" [ -");
156 #ifndef DISABLE_SUPPORT_FOR_REMOTE_CONNECTIONS
|
157 thilo.boehm 1.11 #ifdef PEGASUS_HAS_SSL
|
158 kumpf 1.2 usage.append (_OPTION_SSL);
159 usage.append (" ] [ -");
|
160 thilo.boehm 1.11 #endif
|
161 kumpf 1.2 usage.append (_OPTION_HOSTNAME);
162 usage.append (" hostname ] [ -");
163 usage.append (_OPTION_PORTNUMBER);
164 usage.append (" portnumber ] [ -");
165 usage.append (_OPTION_USERNAME);
166 usage.append (" username ] [ -");
167 usage.append (_OPTION_PASSWORD);
168 usage.append (" password ] [ -");
169 usage.append (_OPTION_TIMEOUT);
170 usage.append (" timeout ] [ -");
171 #endif
172 usage.append (_OPTION_DEBUG);
173 usage.append (" ]");
174 setUsage (usage);
175 }
176
|
177 kumpf 1.17 String IPInfoCommand::_promptForPassword( ostream& outPrintWriter )
|
178 kumpf 1.2 {
|
179 kumpf 1.8 //
180 // Password is not set, prompt for non-blank password
181 //
182 String pw;
183 Uint32 retries = 1;
184 do
|
185 kumpf 1.2 {
|
186 kumpf 1.8 pw = System::getPassword(PASSWORD_PROMPT);
|
187 kumpf 1.2
|
188 kumpf 1.8 if (pw == String::EMPTY)
|
189 kumpf 1.2 {
|
190 kumpf 1.8 if (retries < MAX_PW_RETRIES)
|
191 kumpf 1.2 {
|
192 kumpf 1.8 retries++;
|
193 kumpf 1.2 }
|
194 kumpf 1.8 else
|
195 kumpf 1.2 {
|
196 kumpf 1.8 break;
|
197 kumpf 1.2 }
|
198 kumpf 1.8 outPrintWriter << PASSWORD_BLANK << endl;
199 continue;
|
200 kumpf 1.2 }
201 }
|
202 kumpf 1.8 while (pw == String::EMPTY);
203 return pw;
|
204 kumpf 1.2 }
205
206 /**
|
207 kumpf 1.17
|
208 kumpf 1.2 Connects to cimserver.
|
209 kumpf 1.17
|
210 kumpf 1.2 @param outPrintWriter the ostream to which error output should be
211 written
|
212 kumpf 1.17
|
213 kumpf 1.2 @exception Exception if an error is encountered in creating
214 the connection
|
215 kumpf 1.17
|
216 kumpf 1.2 */
|
217 david.dillard 1.6 void IPInfoCommand::_connectToServer( CIMClient& client,
|
218 karl 1.9 ostream& outPrintWriter )
|
219 kumpf 1.2 {
220 String host = String ();
221 Uint32 portNumber = 0;
222 Boolean connectToLocal = false;
223
224 //
225 // Construct host address
226 //
227
|
228 kumpf 1.17 if ((!_hostNameSet) && (!_portNumberSet)
|
229 karl 1.9 && (!_userNameSet) && (!_passwordSet))
230 {
231 connectToLocal = true;
232 }
|
233 kumpf 1.2 else
234 {
235 if (!_hostNameSet)
236 {
237 _hostName = System::getHostName();
238 }
239 if( !_portNumberSet )
240 {
241 if( _useSSL )
242 {
243 _portNumber = System::lookupPort( WBEM_HTTPS_SERVICE_NAME,
244 WBEM_DEFAULT_HTTPS_PORT );
245 }
246 else
247 {
248 _portNumber = System::lookupPort( WBEM_HTTP_SERVICE_NAME,
249 WBEM_DEFAULT_HTTP_PORT );
250 }
251 char buffer[32];
252 sprintf( buffer, "%lu", (unsigned long) _portNumber );
253 _portNumberStr = buffer;
254 kumpf 1.2 }
255 }
256 host = _hostName;
257 portNumber = _portNumber;
258
259 if( connectToLocal )
260 {
261 client.connectLocal();
262 }
263 else if( _useSSL )
264 {
|
265 thilo.boehm 1.11 #ifdef PEGASUS_HAS_SSL
|
266 kumpf 1.2 //
267 // Get environment variables:
268 //
269 const char* pegasusHome = getenv("PEGASUS_HOME");
|
270 kumpf 1.17
|
271 karl 1.9 String certpath = FileSystem::getAbsolutePath(
272 pegasusHome, PEGASUS_SSLCLIENT_CERTIFICATEFILE);
|
273 kumpf 1.17
|
274 karl 1.9 String randFile;
|
275 thilo.boehm 1.11
276 #ifdef PEGASUS_SSL_RANDOMFILE
|
277 karl 1.9 randFile = FileSystem::getAbsolutePath(
278 pegasusHome, PEGASUS_SSLCLIENT_RANDOMFILE);
|
279 thilo.boehm 1.11 #endif
|
280 kumpf 1.2 SSLContext sslcontext (certpath, verifyCertificate, randFile);
281
282 if (!_userNameSet)
283 {
284 _userName = System::getEffectiveUserName();
285 }
286
287 if (!_passwordSet)
288 {
289 _password = _promptForPassword( outPrintWriter );
290 }
|
291 karl 1.9 client.connect(host, portNumber, sslcontext, _userName, _password );
|
292 thilo.boehm 1.11 #else
|
293 dl.meetei 1.18 PEGASUS_UNREACHABLE(PEGASUS_ASSERT(false);)
|
294 thilo.boehm 1.11 #endif
|
295 kumpf 1.2 }
296 else
|
297 kumpf 1.17 {
|
298 kumpf 1.2 if (!_passwordSet)
299 {
300 _password = _promptForPassword( outPrintWriter );
301 }
302 client.connect(host, portNumber, _userName, _password );
303 }
304 }
305
306 /**
|
307 kumpf 1.17
|
308 kumpf 1.2 Parses the command line, validates the options, and sets instance
309 variables based on the option arguments.
|
310 kumpf 1.17
|
311 kumpf 1.2 @param argc the number of command line arguments
312 @param argv the string vector of command line arguments
|
313 kumpf 1.17
|
314 kumpf 1.2 @exception CommandFormatException if an error is encountered in parsing
315 the command line
|
316 kumpf 1.17
|
317 kumpf 1.2 */
|
318 david.dillard 1.6 void IPInfoCommand::setCommand (Uint32 argc, char* argv [])
|
319 kumpf 1.2 {
320 Uint32 i = 0;
321 Uint32 c = 0;
322 String httpVersion = String ();
323 String httpMethod = String ();
324 String timeoutStr = String ();
325 String GetOptString = String ();
326 getoopt getOpts;
327
328 //
329 // Construct GetOptString
330 //
331 #ifndef DISABLE_SUPPORT_FOR_REMOTE_CONNECTIONS
332 GetOptString.append (_OPTION_HOSTNAME);
333 GetOptString.append (getoopt::GETOPT_ARGUMENT_DESIGNATOR);
334 GetOptString.append (_OPTION_PORTNUMBER);
335 GetOptString.append (getoopt::GETOPT_ARGUMENT_DESIGNATOR);
|
336 thilo.boehm 1.11 #ifdef PEGASUS_HAS_SSL
|
337 kumpf 1.2 GetOptString.append (_OPTION_SSL);
|
338 thilo.boehm 1.11 #endif
|
339 kumpf 1.2 GetOptString.append (_OPTION_TIMEOUT);
340 GetOptString.append (getoopt::GETOPT_ARGUMENT_DESIGNATOR);
341 GetOptString.append (_OPTION_USERNAME);
342 GetOptString.append (getoopt::GETOPT_ARGUMENT_DESIGNATOR);
343 GetOptString.append (_OPTION_PASSWORD);
344 GetOptString.append (getoopt::GETOPT_ARGUMENT_DESIGNATOR);
345 #endif
346 GetOptString.append (_OPTION_DEBUG);
347
348 //
349 // Initialize and parse getOpts
350 //
351 getOpts = getoopt ();
352 getOpts.addFlagspec (GetOptString);
353 getOpts.parse (argc, argv);
354
355 if (getOpts.hasErrors ())
356 {
|
357 kumpf 1.10 throw CommandFormatException(getOpts.getErrorStrings()[0]);
|
358 kumpf 1.2 }
|
359 kumpf 1.17
|
360 kumpf 1.2 //
361 // Get options and arguments from the command line
362 //
363 for (i = getOpts.first (); i < getOpts.last (); i++)
364 {
365 if (getOpts [i].getType () == Optarg::LONGFLAG)
366 {
|
367 kumpf 1.10 throw UnexpectedArgumentException(getOpts[i].Value());
|
368 kumpf 1.17 }
|
369 kumpf 1.2 else if (getOpts [i].getType () == Optarg::REGULAR)
370 {
|
371 kumpf 1.10 throw UnexpectedArgumentException(getOpts[i].Value());
|
372 kumpf 1.17 }
|
373 kumpf 1.2 else /* getOpts [i].getType () == FLAG */
374 {
375 c = getOpts [i].getopt () [0];
|
376 kumpf 1.17
377 switch (c)
|
378 kumpf 1.2 {
|
379 kumpf 1.17 case _OPTION_HOSTNAME:
|
380 kumpf 1.2 {
381 if (getOpts.isSet (_OPTION_HOSTNAME) > 1)
382 {
383 //
384 // More than one hostname option was found
385 //
|
386 kumpf 1.17 throw DuplicateOptionException(_OPTION_HOSTNAME);
|
387 kumpf 1.2 }
388 _hostName = getOpts [i].Value ();
389 _hostNameSet = true;
390 break;
391 }
|
392 kumpf 1.17
393 case _OPTION_PORTNUMBER:
|
394 kumpf 1.2 {
395 if (getOpts.isSet (_OPTION_PORTNUMBER) > 1)
396 {
397 //
398 // More than one portNumber option was found
399 //
|
400 kumpf 1.17 throw DuplicateOptionException(_OPTION_PORTNUMBER);
|
401 kumpf 1.2 }
|
402 kumpf 1.17
|
403 kumpf 1.2 _portNumberStr = getOpts [i].Value ();
|
404 kumpf 1.17
|
405 kumpf 1.2 try
406 {
407 getOpts [i].Value (_portNumber);
408 }
|
409 david.dillard 1.6 catch (const TypeMismatchException&)
|
410 kumpf 1.2 {
|
411 kumpf 1.10 throw InvalidOptionArgumentException(
412 _portNumberStr,
|
413 kumpf 1.2 _OPTION_PORTNUMBER);
414 }
|
415 david.dillard 1.6 _portNumberSet = true;
|
416 kumpf 1.2 break;
417 }
|
418 kumpf 1.17 #ifdef PEGASUS_HAS_SSL
419 case _OPTION_SSL:
|
420 kumpf 1.2 {
421 //
422 // Use port 5989 as the default port for SSL
423 //
|
424 karl 1.9 _useSSL = true;
|
425 kumpf 1.2 if (!_portNumberSet)
426 _portNumber = 5989;
427 break;
428 }
|
429 kumpf 1.17 #endif
|
430 kumpf 1.2 case _OPTION_DEBUG:
431 {
|
432 karl 1.9 _enableDebug = true;
|
433 kumpf 1.2 break;
434 }
|
435 kumpf 1.17
436 case _OPTION_TIMEOUT:
|
437 kumpf 1.2 {
438 if (getOpts.isSet (_OPTION_TIMEOUT) > 1)
439 {
440 //
441 // More than one timeout option was found
442 //
|
443 kumpf 1.17 throw DuplicateOptionException(_OPTION_TIMEOUT);
|
444 kumpf 1.2 }
|
445 kumpf 1.17
|
446 kumpf 1.2 timeoutStr = getOpts [i].Value ();
|
447 kumpf 1.17
|
448 kumpf 1.2 try
449 {
450 getOpts [i].Value (_timeout);
451 }
|
452 david.dillard 1.6 catch (const TypeMismatchException&)
|
453 kumpf 1.2 {
|
454 kumpf 1.10 throw InvalidOptionArgumentException(
455 timeoutStr,
|
456 kumpf 1.2 _OPTION_TIMEOUT);
457 }
458 break;
459 }
|
460 kumpf 1.17
461 case _OPTION_USERNAME:
|
462 kumpf 1.2 {
463 if (getOpts.isSet (_OPTION_USERNAME) > 1)
464 {
465 //
466 // More than one username option was found
467 //
|
468 kumpf 1.17 throw DuplicateOptionException(_OPTION_USERNAME);
|
469 kumpf 1.2 }
470 _userName = getOpts [i].Value ();
471 _userNameSet = true;
472 break;
473 }
|
474 kumpf 1.17
475 case _OPTION_PASSWORD:
|
476 kumpf 1.2 {
477 if (getOpts.isSet (_OPTION_PASSWORD) > 1)
478 {
479 //
480 // More than one password option was found
481 //
|
482 kumpf 1.17 throw DuplicateOptionException(_OPTION_PASSWORD);
|
483 kumpf 1.2 }
484 _password = getOpts [i].Value ();
485 _passwordSet = true;
486 break;
487 }
|
488 kumpf 1.17
|
489 kumpf 1.2 default:
490 //
491 // This path should not be hit
492 //
493 break;
494 }
495 }
496 }
497
498 if (getOpts.isSet (_OPTION_PORTNUMBER) < 1)
499 {
500 //
501 // No portNumber specified
502 // Default to WBEM_DEFAULT_PORT
503 // Already done in constructor
504 //
|
505 kumpf 1.17 }
506 else
|
507 kumpf 1.2 {
508 if (_portNumber > _MAX_PORTNUMBER)
509 {
510 //
511 // Portnumber out of valid range
512 //
|
513 kumpf 1.10 throw InvalidOptionArgumentException(
514 _portNumberStr,
|
515 kumpf 1.2 _OPTION_PORTNUMBER);
516 }
517 }
518
519 if (getOpts.isSet (_OPTION_TIMEOUT) < 1)
520 {
521 //
522 // No timeout specified
523 // Default to DEFAULT_TIMEOUT_MILLISECONDS
524 // Already done in constructor
525 //
|
526 kumpf 1.17 }
527 else
|
528 kumpf 1.2 {
|
529 kumpf 1.17 if (_timeout == 0)
|
530 kumpf 1.2 {
531 //
532 // Timeout out of valid range
533 //
|
534 kumpf 1.10 throw InvalidOptionArgumentException(timeoutStr, _OPTION_TIMEOUT);
|
535 kumpf 1.2 }
536 }
537 }
538
539 /** ErrorExit - Print out the error message and exits.
540 @param errPrintWriter The ostream to which error output should be
541 written
542 @param message Text for error message
|
543 david.dillard 1.6 @return None, Terminates the program
|
544 kumpf 1.2 */
545 void IPInfoCommand::errorExit( ostream& errPrintWriter,
546 const String& message)
547 {
548 errPrintWriter << "ipinfo error: " << message << endl;
549 exit(1);
550 }
551
552 void IPInfoCommand::getIPInfo(ostream& outPrintWriter,
553 ostream& errPrintWriter)
554 {
555
556 CIMClient client;
557 client.setTimeout( _timeout );
558
559 try
560 {
561 _connectToServer( client, outPrintWriter);
562 IPRouteInfo ipr (client, _enableDebug, outPrintWriter, errPrintWriter);
563 IPPEpInfo ippep(client, _enableDebug, outPrintWriter, errPrintWriter);
|
564 karl 1.9 BIPTLEpInfo ipbiptle(client, _enableDebug, outPrintWriter,
565 errPrintWriter);
|
566 mateus.baur 1.12 NextHopIPRouteInfo nhipr (
|
567 kumpf 1.17 client,
568 _enableDebug,
569 outPrintWriter,
|
570 mateus.baur 1.12 errPrintWriter);
|
571 kumpf 1.2
|
572 mateus.baur 1.13 RSApInfo rsap(
573 client,
574 _enableDebug,
575 outPrintWriter,
576 errPrintWriter);
577
|
578 kumpf 1.2 } // end try
579
|
580 david.dillard 1.6 catch(const Exception& e)
|
581 kumpf 1.2 {
|
582 karl 1.9 errorExit(errPrintWriter, e.getMessage());
|
583 kumpf 1.2 }
584
585 }
586
587
588 /**
|
589 kumpf 1.17
|
590 kumpf 1.2 Executes the command and writes the results to the PrintWriters.
|
591 kumpf 1.17
|
592 kumpf 1.2 @param outPrintWriter the ostream to which output should be
593 written
594 @param errPrintWriter the ostream to which error output should be
595 written
|
596 kumpf 1.17
|
597 kumpf 1.2 @return 0 if the command is successful
598 1 if an error occurs in executing the command
|
599 kumpf 1.17
|
600 kumpf 1.2 */
|
601 kumpf 1.17 Uint32 IPInfoCommand::execute (ostream& outPrintWriter,
602 ostream& errPrintWriter)
|
603 kumpf 1.2 {
604 try
605 {
606 IPInfoCommand::getIPInfo( outPrintWriter, errPrintWriter );
607 }
|
608 david.dillard 1.6 catch (const IPInfoException& e)
|
609 kumpf 1.2 {
|
610 kumpf 1.17 errPrintWriter << IPInfoCommand::COMMAND_NAME << ": "
|
611 karl 1.9 << e.getMessage () << endl;
|
612 kumpf 1.2 return (RC_ERROR);
613 }
614 return (RC_SUCCESS);
615 }
616
617 /**
|
618 kumpf 1.17
|
619 kumpf 1.2 Parses the command line, and executes the command.
|
620 kumpf 1.17
|
621 kumpf 1.2 @param argc the number of command line arguments
622 @param argv the string vector of command line arguments
|
623 kumpf 1.17
|
624 kumpf 1.2 @return 0 if the command is successful
625 1 if an error occurs in executing the command
|
626 kumpf 1.17
|
627 kumpf 1.2 */
628 PEGASUS_NAMESPACE_END
629
630 // exclude main from the Pegasus Namespace
631 PEGASUS_USING_PEGASUS;
632 PEGASUS_USING_STD;
633
|
634 kumpf 1.17 int main (int argc, char* argv [])
|
635 kumpf 1.2 {
636 IPInfoCommand command = IPInfoCommand ();
637 int rc;
638
|
639 kumpf 1.17 try
|
640 kumpf 1.2 {
641 command.setCommand (argc, argv);
|
642 kumpf 1.17 }
643 catch (const CommandFormatException& cfe)
|
644 kumpf 1.2 {
|
645 kumpf 1.17 cerr << IPInfoCommand::COMMAND_NAME << ": " << cfe.getMessage ()
|
646 kumpf 1.2 << endl;
647 cerr << command.getUsage () << endl;
648 exit (Command::RC_ERROR);
649 }
650
651 rc = command.execute (cout, cerr);
652 exit (rc);
653 return 0;
654 }
|