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