diff options
Diffstat (limited to 'cpp/src/IceGrid/Client.cpp')
-rw-r--r-- | cpp/src/IceGrid/Client.cpp | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/cpp/src/IceGrid/Client.cpp b/cpp/src/IceGrid/Client.cpp new file mode 100644 index 00000000000..1738873f596 --- /dev/null +++ b/cpp/src/IceGrid/Client.cpp @@ -0,0 +1,796 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceUtil/DisableWarnings.h> +#include <IceUtil/Options.h> +#include <IceUtil/CtrlCHandler.h> +#include <IceUtil/Thread.h> +#include <IceUtil/StringUtil.h> +#include <IceUtil/UUID.h> +#include <IceUtil/Mutex.h> +#include <IceUtil/MutexPtrLock.h> +#include <Ice/Ice.h> +#include <Ice/SliceChecksums.h> +#include <IceGrid/Parser.h> +#include <IceGrid/FileParserI.h> +#include <IceGrid/Registry.h> +#include <IceGrid/Locator.h> +#include <Glacier2/Router.h> +#include <fstream> + +// +// For getPassword() +// +#ifndef _WIN32 +# include <termios.h> +#else +# include <conio.h> +#endif + +using namespace std; +//using namespace Ice; // COMPILERFIX: VC6 reports compilation error because of ambiguous Locator symbol. +using namespace IceGrid; + +class Client; + +namespace +{ + +IceUtil::Mutex* _staticMutex = 0; +Client* _globalClient = 0; + +class Init +{ +public: + + Init() + { + _staticMutex = new IceUtil::Mutex; + } + + ~Init() + { + delete _staticMutex; + _staticMutex = 0; + } +}; + +Init init; + +} + +class SessionKeepAliveThread : public IceUtil::Thread, public IceUtil::Monitor<IceUtil::Mutex> +{ +public: + + SessionKeepAliveThread(const AdminSessionPrx& session, long timeout) : + IceUtil::Thread("IceGrid admin session keepalive thread"), + _session(session), + _timeout(IceUtil::Time::seconds(timeout)), + _destroy(false) + { + } + + virtual void + run() + { + Lock sync(*this); + while(!_destroy) + { + timedWait(_timeout); + if(_destroy) + { + break; + } + try + { + _session->keepAlive(); + } + catch(const Ice::Exception&) + { + break; + } + } + } + + void + destroy() + { + Lock sync(*this); + _destroy = true; + notify(); + } + +private: + + AdminSessionPrx _session; + const IceUtil::Time _timeout; + bool _destroy; +}; +typedef IceUtil::Handle<SessionKeepAliveThread> SessionKeepAliveThreadPtr; + +class ReuseConnectionRouter : public Ice::Router +{ +public: + + ReuseConnectionRouter(const Ice::ObjectPrx& proxy) : _clientProxy(proxy) + { + } + + virtual Ice::ObjectPrx + getClientProxy(const Ice::Current&) const + { + return _clientProxy; + } + + virtual Ice::ObjectPrx + getServerProxy(const Ice::Current&) const + { + return 0; + } + + virtual void + addProxy(const Ice::ObjectPrx&, const Ice::Current&) + { + } + + virtual Ice::ObjectProxySeq + addProxies(const Ice::ObjectProxySeq&, const Ice::Current&) + { + return Ice::ObjectProxySeq(); + } + +private: + + const Ice::ObjectPrx _clientProxy; +}; + +class Client : public IceUtil::Monitor<IceUtil::Mutex> +{ +public: + + void usage(); + int main(Ice::StringSeq& args); + int run(Ice::StringSeq& args); + void interrupted(); + + Ice::CommunicatorPtr communicator() const { return _communicator; } + const string& appName() const { return _appName; } + + string getPassword(const string&); + +private: + + IceUtil::CtrlCHandler _ctrlCHandler; + Ice::CommunicatorPtr _communicator; + string _appName; + ParserPtr _parser; +}; + +static void +interruptCallback(int signal) +{ + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_staticMutex); + if(_globalClient) + { + _globalClient->interrupted(); + } +} + +//COMPILERFIX: Borland C++ 2010 doesn't support wmain for console applications. +#if defined(_WIN32 ) && !defined(__BCPLUSPLUS__) + +int +wmain(int argc, wchar_t* argv[]) + +#else + +int +main(int argc, char* argv[]) + +#endif +{ + Client app; + Ice::StringSeq args = Ice::argsToStringSeq(argc, argv); + return app.main(args); +} + +void +Client::usage() +{ + cerr << "Usage: " << appName() << " [options]\n"; + cerr << + "Options:\n" + "-h, --help Show this message.\n" + "-v, --version Display the Ice version.\n" + "-e COMMANDS Execute COMMANDS.\n" + "-d, --debug Print debug messages.\n" + "-s, --server Start icegridadmin as a server (to parse XML files).\n" + "-u, --username Login with the given username.\n" + "-p, --password Login with the given password.\n" + "-S, --ssl Authenticate through SSL.\n" + "-r, --replica NAME Connect to the replica NAME.\n" + ; +} + + +int +Client::main(Ice::StringSeq& args) +{ + int status = EXIT_SUCCESS; + + try + { + _appName = args[0]; + Ice::InitializationData id; + id.properties = Ice::createProperties(args); + // + // We don't want to load DB plug-ins with icegridadmin, as this will + // cause FileLock issues when run with the same configuration file + // used by the service. + // + id.properties->setProperty("Ice.Plugin.DB", ""); + _communicator = Ice::initialize(id); + + { + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(_staticMutex); + _globalClient = this; + } + _ctrlCHandler.setCallback(interruptCallback); + + try + { + status = run(args); + } + catch(const Ice::CommunicatorDestroyedException&) + { + // Expected if the client is interrupted during the initialization. + } + } + catch(const IceUtil::Exception& ex) + { + cerr << _appName << ": " << ex << endl; + status = EXIT_FAILURE; + } + catch(const std::exception& ex) + { + cerr << _appName << ": std::exception: " << ex.what() << endl; + status = EXIT_FAILURE; + } + catch(const std::string& msg) + { + cerr << _appName << ": " << msg << endl; + status = EXIT_FAILURE; + } + catch(const char* msg) + { + cerr << _appName << ": " << msg << endl; + status = EXIT_FAILURE; + } + catch(...) + { + cerr << _appName << ": unknown exception" << endl; + status = EXIT_FAILURE; + } + + if(_communicator) + { + try + { + _communicator->destroy(); + } + catch(const Ice::CommunicatorDestroyedException&) + { + } + catch(const Ice::Exception& ex) + { + cerr << ex << endl; + status = EXIT_FAILURE; + } + } + + _ctrlCHandler.setCallback(0); + { + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(_staticMutex); + _globalClient = 0; + } + + return status; + +} + +void +Client::interrupted() +{ + Lock sync(*this); + if(_parser) // If there's an interactive parser, notify the parser. + { + _parser->interrupt(); + } + else + { + // + // Otherwise, destroy the communicator. + // + assert(_communicator); + try + { + _communicator->destroy(); + } + catch(const Ice::Exception&) + { + } + } +} + +int +Client::run(Ice::StringSeq& originalArgs) +{ + string commands; + bool debug; + + IceUtilInternal::Options opts; + opts.addOpt("h", "help"); + opts.addOpt("v", "version"); + opts.addOpt("e", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat); + opts.addOpt("u", "username", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat); + opts.addOpt("p", "password", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat); + opts.addOpt("S", "ssl"); + opts.addOpt("d", "debug"); + opts.addOpt("s", "server"); + opts.addOpt("r", "replica", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::NoRepeat); + + vector<string> args; + try + { + args = opts.parse(originalArgs); + } + catch(const IceUtilInternal::BadOptException& e) + { + cerr << e.reason << endl; + usage(); + return EXIT_FAILURE; + } + if(!args.empty()) + { + cerr << _appName << ": too many arguments" << endl; + usage(); + return EXIT_FAILURE; + } + + if(opts.isSet("help")) + { + usage(); + return EXIT_SUCCESS; + } + if(opts.isSet("version")) + { + cout << ICE_STRING_VERSION << endl; + return EXIT_SUCCESS; + } + + if(opts.isSet("server")) + { + Ice::ObjectAdapterPtr adapter = + communicator()->createObjectAdapterWithEndpoints("FileParser", "tcp -h localhost"); + adapter->activate(); + Ice::ObjectPrx proxy = adapter->add(new FileParserI, communicator()->stringToIdentity("FileParser")); + cout << proxy << endl; + + communicator()->waitForShutdown(); + return EXIT_SUCCESS; + } + + if(opts.isSet("e")) + { + vector<string> optargs = opts.argVec("e"); + for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i) + { + commands += *i + ";"; + } + } + debug = opts.isSet("debug"); + + bool ssl = communicator()->getProperties()->getPropertyAsInt("IceGridAdmin.AuthenticateUsingSSL"); + if(opts.isSet("ssl")) + { + ssl = true; + } + + string id = communicator()->getProperties()->getProperty("IceGridAdmin.Username"); + if(!opts.optArg("username").empty()) + { + id = opts.optArg("username"); + } + string password = communicator()->getProperties()->getProperty("IceGridAdmin.Password"); + if(!opts.optArg("password").empty()) + { + password = opts.optArg("password"); + } + + Ice::PropertiesPtr properties = communicator()->getProperties(); + string replica = properties->getProperty("IceGridAdmin.Replica"); + if(!opts.optArg("replica").empty()) + { + replica = opts.optArg("replica"); + } + + Glacier2::RouterPrx router; + AdminSessionPrx session; + SessionKeepAliveThreadPtr keepAlive; + int status = EXIT_SUCCESS; + try + { + int timeout; + if(communicator()->getDefaultRouter()) + { + try + { + // Use SSL if available. + router = Glacier2::RouterPrx::checkedCast(communicator()->getDefaultRouter()->ice_preferSecure(true)); + if(!router) + { + cerr << _appName << ": configured router is not a Glacier2 router" << endl; + return EXIT_FAILURE; + } + } + catch(const Ice::LocalException& ex) + { + cerr << _appName << ": could not contact the default router:" << endl << ex << endl; + return EXIT_FAILURE; + } + + if(ssl) + { + session = AdminSessionPrx::uncheckedCast(router->createSessionFromSecureConnection()); + if(!session) + { + cerr << _appName + << ": Glacier2 returned a null session, please set the Glacier2.SSLSessionManager property" + << endl; + return EXIT_FAILURE; + } + } + else + { + while(id.empty() && cin.good()) + { + cout << "user id: " << flush; + getline(cin, id); + if(!cin.good()) + { + return EXIT_FAILURE; + } + id = IceUtilInternal::trim(id); + } + + if(password.empty()) + { + password = getPassword("password: "); +#ifndef _WIN32 + if(!cin.good()) + { + return EXIT_FAILURE; + } +#endif + } + + session = AdminSessionPrx::uncheckedCast(router->createSession(id, password)); + // Zero the password string. + for(string::iterator p = password.begin(); p != password.end(); ++p) + { + *p = '\0'; + } + if(!session) + { + cerr << _appName + << ": Glacier2 returned a null session, please set the Glacier2.SessionManager property" + << endl; + return EXIT_FAILURE; + } + } + timeout = static_cast<int>(router->getSessionTimeout()); + } + else if(communicator()->getDefaultLocator()) + { + // + // Create the identity of the registry to connect to. + // + Ice::Identity registryId; + registryId.category = communicator()->getDefaultLocator()->ice_getIdentity().category; + registryId.name = "Registry"; + if(!replica.empty() && replica != "Master") + { + registryId.name += "-" + replica; + } + + // + // First try to contact the locator. If we can't talk to the locator, + // no need to go further. Otherwise, we get the proxy of local registry + // proxy. + // + IceGrid::LocatorPrx locator; + RegistryPrx localRegistry; + try + { + locator = IceGrid::LocatorPrx::checkedCast(communicator()->getDefaultLocator()); + if(!locator) + { + cerr << _appName << ": configured locator is not an IceGrid locator" << endl; + return EXIT_FAILURE; + } + localRegistry = locator->getLocalRegistry(); + } + catch(const Ice::LocalException& ex) + { + cerr << _appName << ": could not contact the default locator:" << endl << ex << endl; + return EXIT_FAILURE; + } + + IceGrid::RegistryPrx registry; + if(localRegistry->ice_getIdentity() == registryId) + { + registry = localRegistry; + } + else + { + // + // The locator local registry isn't the registry we want to connect to. + // + + try + { + const string strId = "\"" + communicator()->identityToString(registryId) + "\""; + registry = RegistryPrx::checkedCast(communicator()->stringToProxy(strId)); + if(!registry) + { + cerr << _appName << ": could not contact an IceGrid registry" << endl; + } + } + catch(const Ice::NotRegisteredException&) + { + cerr << _appName << ": no active registry replica named `" << replica << "'" << endl; + return EXIT_FAILURE; + } + catch(const Ice::LocalException& ex) + { + if(!replica.empty()) + { + cerr << _appName << ": could not contact the registry replica named `" << replica << "':\n"; + cerr << ex << endl; + return EXIT_FAILURE; + } + else + { + // + // If we can't contact the master, use to the local registry. + // + registry = localRegistry; + string name = registry->ice_getIdentity().name; + const string prefix("Registry-"); + string::size_type pos = name.find(prefix); + if(pos != string::npos) + { + name = name.substr(prefix.size()); + } + cerr << _appName << ": warning: could not contact master, using slave `" << name << "'" << endl; + } + } + } + + // + // If the registry to use is the locator local registry, we install a default router + // to ensure we'll use a single connection regardless of the endpoints returned in the + // proxies of the various session/admin methods (useful if used over an ssh tunnel). + // + if(registry->ice_getIdentity() == localRegistry->ice_getIdentity()) + { + properties->setProperty("CollocInternal.AdapterId", IceUtil::generateUUID()); + Ice::ObjectAdapterPtr colloc = communicator()->createObjectAdapter("CollocInternal"); + colloc->setLocator(0); + Ice::ObjectPrx router = colloc->addWithUUID(new ReuseConnectionRouter(locator)); + communicator()->setDefaultRouter(Ice::RouterPrx::uncheckedCast(router)); + registry = registry->ice_router(communicator()->getDefaultRouter()); + } + + // Prefer SSL. + registry = registry->ice_preferSecure(true); + + if(ssl) + { + session = registry->createAdminSessionFromSecureConnection(); + } + else + { + while(id.empty() && cin.good()) + { + cout << "user id: " << flush; + getline(cin, id); + if(!cin.good()) + { + return EXIT_FAILURE; + } + id = IceUtilInternal::trim(id); + } + + if(password.empty()) + { + password = getPassword("password: "); +#ifndef _WIN32 + if(!cin.good()) + { + return EXIT_FAILURE; + } +#endif + } + + session = registry->createAdminSession(id, password); + // Zero the password string. + for(string::iterator p = password.begin(); p != password.end(); ++p) + { + *p = '\0'; + } + } + + timeout = registry->getSessionTimeout(); + } + else // No default locator or router set. + { + cerr << _appName << ": could not contact the registry:" << endl; + cerr << "no default locator or router configured" << endl; + return EXIT_FAILURE; + } + + if(timeout > 0) + { + keepAlive = new SessionKeepAliveThread(session, timeout / 2); + keepAlive->start(); + } + + AdminPrx admin = session->getAdmin(); + + Ice::SliceChecksumDict serverChecksums = admin->getSliceChecksums(); + Ice::SliceChecksumDict localChecksums = Ice::sliceChecksums(); + + // + // The following slice types are only used by the admin CLI. + // + localChecksums.erase("::IceGrid::FileParser"); + localChecksums.erase("::IceGrid::ParseException"); + + for(Ice::SliceChecksumDict::const_iterator q = localChecksums.begin(); q != localChecksums.end(); ++q) + { + Ice::SliceChecksumDict::const_iterator r = serverChecksums.find(q->first); + if(r == serverChecksums.end()) + { + cerr << appName() << ": server is using unknown Slice type `" << q->first << "'" << endl; + } + else if(q->second != r->second) + { + cerr << appName() << ": server is using a different Slice definition of `" << q->first << "'" << endl; + } + } + + { + Lock sync(*this); + _parser = Parser::createParser(communicator(), session, admin, commands.empty()); + } + + if(!commands.empty()) // Commands were given + { + int parseStatus = _parser->parse(commands, debug); + if(parseStatus == EXIT_FAILURE) + { + status = EXIT_FAILURE; + } + } + else // No commands, let's use standard input + { + _parser->showBanner(); + + int parseStatus = _parser->parse(stdin, debug); + if(parseStatus == EXIT_FAILURE) + { + status = EXIT_FAILURE; + } + } + } + catch(const IceGrid::PermissionDeniedException& ex) + { + cout << "permission denied:\n" << ex.reason << endl; + return EXIT_FAILURE; + } + catch(const Glacier2::PermissionDeniedException& ex) + { + cout << "permission denied:\n" << ex.reason << endl; + return EXIT_FAILURE; + } + catch(const Glacier2::CannotCreateSessionException& ex) + { + cout << "session creation failed:\n" << ex.reason << endl; + return EXIT_FAILURE; + } + catch(...) + { + if(keepAlive) + { + keepAlive->destroy(); + keepAlive->getThreadControl().join(); + } + + try + { + if(router) + { + router->destroySession(); + } + else + { + session->destroy(); + } + } + catch(const Ice::Exception&) + { + } + throw; + } + + if(keepAlive) + { + keepAlive->destroy(); + keepAlive->getThreadControl().join(); + } + + if(session) + { + try + { + if(router) + { + router->destroySession(); + } + else + { + session->destroy(); + } + } + catch(const Ice::Exception&) + { + // Ignore. If the registry has been shutdown this will cause + // an exception. + } + } + + return status; +} + +string +Client::getPassword(const string& prompt) +{ + cout << prompt << flush; + string password; +#ifndef _WIN32 + struct termios oldConf; + struct termios newConf; + tcgetattr(0, &oldConf); + newConf = oldConf; + newConf.c_lflag &= (~ECHO); + tcsetattr(0, TCSANOW, &newConf); + getline(cin, password); + tcsetattr(0, TCSANOW, &oldConf); +#else + char c; + while((c = _getch()) != '\r') + { + password += c; + } +#endif + cout << endl; + return IceUtilInternal::trim(password); +} |