1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4
5 #include <IceUtil/Options.h>
6 #include <IceUtil/CtrlCHandler.h>
7 #include <IceUtil/Thread.h>
8 #include <IceUtil/StringUtil.h>
9 #include <Ice/ConsoleUtil.h>
10 #include <Ice/UUID.h>
11 #include <IceUtil/Mutex.h>
12 #include <IceUtil/MutexPtrLock.h>
13 #include <Ice/Ice.h>
14 #include <Ice/SliceChecksums.h>
15 #include <IceGrid/Parser.h>
16 #include <IceGrid/FileParserI.h>
17 #include <IceGrid/Registry.h>
18 #include <IceLocatorDiscovery/Plugin.h>
19 #include <Glacier2/Router.h>
20 #include <fstream>
21
22 //
23 // For getPassword()
24 //
25 #ifndef _WIN32
26 # include <termios.h>
27 #else
28 # include <conio.h>
29 # include <fcntl.h>
30 # include <io.h>
31 #endif
32
33 using namespace std;
34 using namespace IceInternal;
35
36 class Client;
37
38 namespace
39 {
40
41 IceUtil::Mutex* _staticMutex = 0;
42 Ice::CommunicatorPtr communicator;
43 IceGrid::ParserPtr parser;
44
45 class Init
46 {
47 public:
48
Init()49 Init()
50 {
51 _staticMutex = new IceUtil::Mutex;
52 }
53
~Init()54 ~Init()
55 {
56 delete _staticMutex;
57 _staticMutex = 0;
58 }
59 };
60
61 Init init;
62
63 }
64
65 class ReuseConnectionRouter : public Ice::Router
66 {
67 public:
68
ReuseConnectionRouter(const Ice::ObjectPrx & proxy)69 ReuseConnectionRouter(const Ice::ObjectPrx& proxy) : _clientProxy(proxy)
70 {
71 }
72
73 virtual Ice::ObjectPrx
getClientProxy(IceUtil::Optional<bool> & hasRoutingTable,const Ice::Current &) const74 getClientProxy(IceUtil::Optional<bool>& hasRoutingTable, const Ice::Current&) const
75 {
76 hasRoutingTable = false;
77 return _clientProxy;
78 }
79
80 virtual Ice::ObjectPrx
getServerProxy(const Ice::Current &) const81 getServerProxy(const Ice::Current&) const
82 {
83 return 0;
84 }
85
86 virtual void
addProxy(const Ice::ObjectPrx &,const Ice::Current &)87 addProxy(const Ice::ObjectPrx&, const Ice::Current&)
88 {
89 }
90
91 virtual Ice::ObjectProxySeq
addProxies(const Ice::ObjectProxySeq &,const Ice::Current &)92 addProxies(const Ice::ObjectProxySeq&, const Ice::Current&)
93 {
94 return Ice::ObjectProxySeq();
95 }
96
97 private:
98
99 const Ice::ObjectPrx _clientProxy;
100 };
101
102 int run(const Ice::StringSeq&);
103
104 //
105 // Callback for CtrlCHandler
106 //
107 static void
interruptCallback(int)108 interruptCallback(int /*signal*/)
109 {
110 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_staticMutex);
111 if(parser) // If there's an interactive parser, notify the parser.
112 {
113 parser->interrupt();
114 }
115 else
116 {
117 //
118 // Otherwise, destroy the communicator.
119 //
120 assert(communicator);
121 communicator->destroy();
122 }
123 }
124
125 int
126 #ifdef _WIN32
wmain(int argc,wchar_t * argv[])127 wmain(int argc, wchar_t* argv[])
128 {
129 //
130 // Enable binary input mode for stdin to avoid automatic conversions.
131 //
132 _setmode(_fileno(stdin), _O_BINARY);
133 #else
134 main(int argc, char* argv[])
135 {
136 #endif
137 int status = 0;
138 Ice::StringSeq args = Ice::argsToStringSeq(argc, argv);
139
140 try
141 {
142 Ice::CtrlCHandler ctrlCHandler;
143 Ice::PropertiesPtr defaultProps = Ice::createProperties();
144 defaultProps->setProperty("IceGridAdmin.Server.Endpoints", "tcp -h localhost");
145 Ice::InitializationData id;
146 id.properties = createProperties(args, defaultProps);
147 id.properties->setProperty("Ice.Warn.Endpoints", "0");
148 Ice::CommunicatorHolder ich(id);
149 communicator = ich.communicator();
150
151 ctrlCHandler.setCallback(interruptCallback);
152
153 try
154 {
155 status = run(args);
156 }
157 catch(const Ice::CommunicatorDestroyedException&)
158 {
159 // Expected if the client is interrupted during the initialization.
160 }
161 }
162 catch(const IceUtil::Exception& ex)
163 {
164 consoleErr << args[0] << ": " << ex << endl;
165 status = 1;
166 }
167 catch(const std::exception& ex)
168 {
169 consoleErr << args[0] << ": std::exception: " << ex.what() << endl;
170 status = 1;
171 }
172 catch(...)
173 {
174 consoleErr << args[0] << ": unknown exception" << endl;
175 status = 1;
176 }
177
178 return status;
179 }
180
181 void
182 usage(const string& name)
183 {
184 consoleErr << "Usage: " << name << " [options]\n";
185 consoleErr <<
186 "Options:\n"
187 "-h, --help Show this message.\n"
188 "-v, --version Display the Ice version.\n"
189 "-e COMMANDS Execute COMMANDS.\n"
190 "-d, --debug Print debug messages.\n"
191 "-s, --server Start icegridadmin as a server (to parse XML files).\n"
192 "-i, --instanceName Connect to the registry with the given instance name.\n"
193 "-H, --host Connect to the registry at the given host.\n"
194 "-P, --port Connect to the registry running on the given port.\n"
195 "-u, --username Login with the given username.\n"
196 "-p, --password Login with the given password.\n"
197 "-S, --ssl Authenticate through SSL.\n"
198 "-r, --replica NAME Connect to the replica NAME.\n"
199 ;
200 }
201
202 string
203 getPassword(const string& prompt)
204 {
205 consoleOut << prompt << flush;
206 string password;
207 #ifndef _WIN32
208 struct termios oldConf;
209 struct termios newConf;
210 tcgetattr(0, &oldConf);
211 newConf = oldConf;
212 newConf.c_lflag &= (~ECHO);
213 tcsetattr(0, TCSANOW, &newConf);
214 getline(cin, password);
215 tcsetattr(0, TCSANOW, &oldConf);
216 #else
217 char c;
218 while((c = static_cast<char>(_getch())) != '\r')
219 {
220 password += c;
221 }
222 #endif
223 consoleOut << endl;
224 return IceUtilInternal::trim(password);
225 }
226
227 extern "C" ICE_LOCATOR_DISCOVERY_API Ice::Plugin*
228 createIceLocatorDiscovery(const Ice::CommunicatorPtr&, const string&, const Ice::StringSeq&);
229
230 int
231 run(const Ice::StringSeq& args)
232 {
233 string commands;
234 bool debug;
235
236 IceUtilInternal::Options opts;
237 opts.addOpt("h", "help");
238 opts.addOpt("v", "version");
239 opts.addOpt("e", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
240 opts.addOpt("i", "instanceName", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat);
241 opts.addOpt("H", "host", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat);
242 opts.addOpt("P", "port", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat);
243 opts.addOpt("u", "username", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat);
244 opts.addOpt("p", "password", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat);
245 opts.addOpt("S", "ssl");
246 opts.addOpt("d", "debug");
247 opts.addOpt("s", "server");
248 opts.addOpt("r", "replica", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat);
249
250 try
251 {
252 if(!opts.parse(args).empty())
253 {
254 consoleErr << args[0] << ": too many arguments" << endl;
255 usage(args[0]);
256 return 1;
257 }
258 }
259 catch(const IceUtilInternal::BadOptException& e)
260 {
261 consoleErr << e.reason << endl;
262 usage(args[0]);
263 return 1;
264 }
265
266 if(opts.isSet("help"))
267 {
268 usage(args[0]);
269 return 0;
270 }
271 if(opts.isSet("version"))
272 {
273 consoleOut << ICE_STRING_VERSION << endl;
274 return 0;
275 }
276
277 if(opts.isSet("server"))
278 {
279 Ice::ObjectAdapterPtr adapter = communicator->createObjectAdapter("IceGridAdmin.Server");
280 adapter->activate();
281 Ice::ObjectPrx proxy = adapter->add(new FileParserI, Ice::stringToIdentity("FileParser"));
282 consoleOut << proxy << endl;
283
284 communicator->waitForShutdown();
285 return 0;
286 }
287
288 if(opts.isSet("e"))
289 {
290 vector<string> optargs = opts.argVec("e");
291 for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
292 {
293 commands += *i + ";";
294 }
295 }
296 debug = opts.isSet("debug");
297
298 bool ssl = communicator->getProperties()->getPropertyAsInt("IceGridAdmin.AuthenticateUsingSSL") > 0;
299 if(opts.isSet("ssl"))
300 {
301 ssl = true;
302 }
303
304 string id = communicator->getProperties()->getProperty("IceGridAdmin.Username");
305 if(!opts.optArg("username").empty())
306 {
307 id = opts.optArg("username");
308 }
309 string password = communicator->getProperties()->getProperty("IceGridAdmin.Password");
310 if(!opts.optArg("password").empty())
311 {
312 password = opts.optArg("password");
313 }
314
315 string host = communicator->getProperties()->getProperty("IceGridAdmin.Host");
316 if(!opts.optArg("host").empty())
317 {
318 host = opts.optArg("host");
319 }
320
321 string instanceName = communicator->getProperties()->getProperty("IceGridAdmin.InstanceName");
322 if(!opts.optArg("instanceName").empty())
323 {
324 instanceName = opts.optArg("instanceName");
325 }
326
327 int port = communicator->getProperties()->getPropertyAsInt("IceGridAdmin.Port");
328 if(!opts.optArg("port").empty())
329 {
330 istringstream is(opts.optArg("port"));
331 if(!(is >> port))
332 {
333 consoleErr << args[0] << ": given port number is not a numeric value" << endl;
334 return 1;
335 }
336 }
337
338 Ice::PropertiesPtr properties = communicator->getProperties();
339 string replica = properties->getProperty("IceGridAdmin.Replica");
340 if(!opts.optArg("replica").empty())
341 {
342 replica = opts.optArg("replica");
343 }
344
345 Glacier2::RouterPrx router;
346 IceGrid::AdminSessionPrx session;
347 int status = 0;
348 try
349 {
350 int acmTimeout = 0;
351 if(!communicator->getDefaultLocator() && !communicator->getDefaultRouter())
352 {
353 if(!host.empty())
354 {
355 const int timeout = 3000; // 3s connection timeout.
356 ostringstream os;
357 os << "Ice/LocatorFinder" << (ssl ? " -s" : "");
358 os << ":tcp -h \"" << host << "\" -p " << (port == 0 ? 4061 : port) << " -t " << timeout;
359 os << ":ssl -h \"" << host << "\" -p " << (port == 0 ? 4062 : port) << " -t " << timeout;
360 Ice::LocatorFinderPrx finder = Ice::LocatorFinderPrx::uncheckedCast(communicator->stringToProxy(os.str()));
361 try
362 {
363 communicator->setDefaultLocator(finder->getLocator());
364 }
365 catch(const Ice::LocalException&)
366 {
367 // Ignore.
368 }
369 if(!instanceName.empty() &&
370 communicator->getDefaultLocator()->ice_getIdentity().category != instanceName)
371 {
372 consoleErr << args[0] << ": registry running on `" << host << "' uses a different instance name:\n";
373 consoleErr << communicator->getDefaultLocator()->ice_getIdentity().category << endl;
374 return 1;
375 }
376 }
377 else
378 {
379 //
380 // NOTE: we don't configure the plugin with the Ice communicator on initialization
381 // because it would install a default locator. Instead, we create the plugin here
382 // to lookup for locator proxies. We destroy the plugin, once we have selected a
383 // locator.
384 //
385 Ice::PluginPtr pluginObj = createIceLocatorDiscovery(communicator, "IceGridAdmin.Discovery", Ice::StringSeq());
386 IceLocatorDiscovery::PluginPtr plugin = IceLocatorDiscovery::PluginPtr::dynamicCast(pluginObj);
387 plugin->initialize();
388
389 vector<Ice::LocatorPrx> locators = plugin->getLocators(instanceName, IceUtil::Time::milliSeconds(300));
390 if(locators.size() > 1)
391 {
392 consoleOut << "found " << locators.size() << " Ice locators:" << endl;
393 unsigned int num = 0;
394 for(vector<Ice::LocatorPrx>::const_iterator p = locators.begin(); p != locators.end(); ++p)
395 {
396 consoleOut << ++num << ": proxy = `" << *p << "'" << endl;
397 }
398
399 num = 0;
400 while(num == 0 && cin.good())
401 {
402 consoleOut << "please enter the locator number to use: " << flush;
403 string line;
404 getline(cin, line);
405 if(!cin.good() || line.empty())
406 {
407 return 1;
408 }
409 line = IceUtilInternal::trim(line);
410
411 istringstream is(line);
412 is >> num;
413 if(num > locators.size())
414 {
415 num = 0;
416 }
417 }
418
419 assert(num <= locators.size());
420 communicator->setDefaultLocator(locators[num - 1]);
421 }
422 else if(locators.size() == 1)
423 {
424 consoleOut << "using discovered locator:\nproxy = `" << locators[0] << "'" << endl;
425 communicator->setDefaultLocator(locators[0]);
426 }
427 else
428 {
429 communicator->setDefaultLocator(0);
430 }
431
432 //
433 // Destroy the plugin, we no longer need it.
434 //
435 plugin->destroy();
436 }
437 }
438
439 if(communicator->getDefaultRouter())
440 {
441 try
442 {
443 // Use SSL if available.
444 router = Glacier2::RouterPrx::checkedCast(communicator->getDefaultRouter()->ice_preferSecure(true));
445 if(!router)
446 {
447 consoleErr << args[0] << ": configured router is not a Glacier2 router" << endl;
448 return 1;
449 }
450 }
451 catch(const Ice::LocalException& ex)
452 {
453 consoleErr << args[0] << ": could not contact the default router:" << endl << ex << endl;
454 return 1;
455 }
456
457 if(ssl)
458 {
459 session = IceGrid::AdminSessionPrx::uncheckedCast(router->createSessionFromSecureConnection());
460 if(!session)
461 {
462 consoleErr << args[0]
463 << ": Glacier2 returned a null session, please set the Glacier2.SSLSessionManager property"
464 << endl;
465 return 1;
466 }
467 }
468 else
469 {
470 while(id.empty() && cin.good())
471 {
472 consoleOut << "user id: " << flush;
473 getline(cin, id);
474 if(!cin.good())
475 {
476 return 1;
477 }
478 id = IceUtilInternal::trim(id);
479 }
480
481 if(password.empty())
482 {
483 password = getPassword("password: ");
484 #ifndef _WIN32
485 if(!cin.good())
486 {
487 return 1;
488 }
489 #endif
490 }
491
492 session = IceGrid::AdminSessionPrx::uncheckedCast(router->createSession(id, password));
493 fill(password.begin(), password.end(), '\0'); // Zero the password string.
494
495 if(!session)
496 {
497 consoleErr << args[0]
498 << ": Glacier2 returned a null session, please set the Glacier2.SessionManager property"
499 << endl;
500 return 1;
501 }
502 }
503
504 try
505 {
506 acmTimeout = router->getACMTimeout();
507 }
508 catch(const Ice::OperationNotExistException&)
509 {
510 consoleErr << args[0] << ": can't talk to old Glacier2 router version" << endl;
511 return 1;
512 }
513 }
514 else if(communicator->getDefaultLocator())
515 {
516 //
517 // Create the identity of the registry to connect to.
518 //
519 Ice::Identity registryId;
520 registryId.category = communicator->getDefaultLocator()->ice_getIdentity().category;
521 registryId.name = "Registry";
522 if(!replica.empty() && replica != "Master")
523 {
524 registryId.name += "-" + replica;
525 }
526
527 //
528 // First try to contact the locator. If we can't talk to the locator,
529 // no need to go further. Otherwise, we get the proxy of local registry
530 // proxy.
531 //
532 IceGrid::LocatorPrx locator;
533 IceGrid::RegistryPrx localRegistry;
534 try
535 {
536 locator = IceGrid::LocatorPrx::checkedCast(communicator->getDefaultLocator());
537 if(!locator)
538 {
539 consoleErr << args[0] << ": configured locator is not an IceGrid locator" << endl;
540 return 1;
541 }
542 localRegistry = locator->getLocalRegistry();
543 }
544 catch(const Ice::LocalException& ex)
545 {
546 consoleErr << args[0] << ": could not contact the default locator:" << endl << ex << endl;
547 return 1;
548 }
549
550 IceGrid::RegistryPrx registry;
551 if(localRegistry->ice_getIdentity() == registryId)
552 {
553 registry = localRegistry;
554 }
555 else
556 {
557 //
558 // The locator local registry isn't the registry we want to connect to.
559 //
560
561 try
562 {
563 registry = IceGrid::RegistryPrx::checkedCast(locator->findObjectById(registryId));
564 if(!registry)
565 {
566 consoleErr << args[0] << ": could not contact an IceGrid registry" << endl;
567 }
568 }
569 catch(const Ice::ObjectNotFoundException&)
570 {
571 consoleErr << args[0] << ": no active registry replica named `" << replica << "'" << endl;
572 return 1;
573 }
574 catch(const Ice::LocalException& ex)
575 {
576 if(!replica.empty())
577 {
578 consoleErr << args[0] << ": could not contact the registry replica named `" << replica << "':\n";
579 consoleErr << ex << endl;
580 return 1;
581 }
582 else
583 {
584 //
585 // If we can't contact the master, use the local registry.
586 //
587 registry = localRegistry;
588 string name = registry->ice_getIdentity().name;
589 const string prefix("Registry-");
590 string::size_type pos = name.find(prefix);
591 if(pos != string::npos)
592 {
593 name = name.substr(prefix.size());
594 }
595 consoleErr << args[0] << ": warning: could not contact master, using slave `" << name << "'" << endl;
596 }
597 }
598 }
599
600 //
601 // If the registry to use is the locator local registry, we install a default router
602 // to ensure we'll use a single connection regardless of the endpoints returned in the
603 // proxies of the various session/admin methods (useful if used over an ssh tunnel).
604 //
605 if(registry->ice_getIdentity() == localRegistry->ice_getIdentity())
606 {
607 Ice::ObjectAdapterPtr colloc = communicator->createObjectAdapter(""); // colloc-only adapter
608 communicator->setDefaultRouter(Ice::RouterPrx::uncheckedCast(
609 colloc->addWithUUID(new ReuseConnectionRouter(locator))));
610 registry = registry->ice_router(communicator->getDefaultRouter());
611 }
612
613 // Prefer SSL.
614 registry = registry->ice_preferSecure(true);
615
616 if(ssl)
617 {
618 session = registry->createAdminSessionFromSecureConnection();
619 }
620 else
621 {
622 while(id.empty() && cin.good())
623 {
624 consoleOut << "user id: " << flush;
625 getline(cin, id);
626 if(!cin.good())
627 {
628 return 1;
629 }
630 id = IceUtilInternal::trim(id);
631 }
632
633 if(password.empty())
634 {
635 password = getPassword("password: ");
636 #ifndef _WIN32
637 if(!cin.good())
638 {
639 return 1;
640 }
641 #endif
642 }
643
644 session = registry->createAdminSession(id, password);
645 fill(password.begin(), password.end(), '\0'); // Zero the password string.
646 }
647
648 try
649 {
650 acmTimeout = registry->getACMTimeout();
651 }
652 catch(const Ice::OperationNotExistException&)
653 {
654 consoleErr << args[0] << ": can't talk to old IceGrid registry version" << endl;
655 return 1;
656 }
657 }
658 else // No default locator or router set.
659 {
660 consoleErr << args[0] << ": could not contact the registry:" << endl;
661 consoleErr << "no default locator or router configured" << endl;
662 return 1;
663 }
664
665 if(acmTimeout > 0)
666 {
667 session->ice_getConnection()->setACM(acmTimeout, IceUtil::None, Ice::ICE_ENUM(ACMHeartbeat, HeartbeatAlways));
668 }
669
670 {
671 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_staticMutex);
672 parser = IceGrid::Parser::createParser(communicator, session, session->getAdmin(), commands.empty());
673 }
674
675 if(!commands.empty()) // Commands were given
676 {
677 int parseStatus = parser->parse(commands, debug);
678 if(parseStatus == 1)
679 {
680 status = 1;
681 }
682 }
683 else // No commands, let's use standard input
684 {
685 parser->showBanner();
686
687 int parseStatus = parser->parse(stdin, debug);
688 if(parseStatus == 1)
689 {
690 status = 1;
691 }
692 }
693 }
694 catch(const IceGrid::PermissionDeniedException& ex)
695 {
696 consoleOut << "permission denied:\n" << ex.reason << endl;
697 return 1;
698 }
699 catch(const Glacier2::PermissionDeniedException& ex)
700 {
701 consoleOut << "permission denied:\n" << ex.reason << endl;
702 return 1;
703 }
704 catch(const Glacier2::CannotCreateSessionException& ex)
705 {
706 consoleOut << "session creation failed:\n" << ex.reason << endl;
707 return 1;
708 }
709 catch(...)
710 {
711 try
712 {
713 if(router)
714 {
715 router->destroySession();
716 }
717 else if(session)
718 {
719 session->destroy();
720 }
721 }
722 catch(const Ice::Exception&)
723 {
724 }
725 throw;
726 }
727
728 if(session)
729 {
730 try
731 {
732 if(router)
733 {
734 router->destroySession();
735 }
736 else
737 {
738 session->destroy();
739 }
740 }
741 catch(const Ice::Exception&)
742 {
743 // Ignore. If the registry has been shutdown this will cause
744 // an exception.
745 }
746 }
747
748 return status;
749 }
750