From 8b2a282eb5b4e274746a89be79a7606354163181 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 8 Jun 2002 23:24:40 +0000 Subject: [PATCH] added aliases to configuration. an alias is another name for a screen. it's expected that the server will want to accept a given client under several names (e.g. the hostname and the FQDN). --- server/CConfig.cpp | 209 +++++++++++++++++++++++++++++++++++++++++---- server/CConfig.h | 44 ++++++++-- server/CServer.cpp | 77 +++++++++-------- server/server.cpp | 9 +- 4 files changed, 274 insertions(+), 65 deletions(-) diff --git a/server/CConfig.cpp b/server/CConfig.cpp index c458c2b1..84f8789c 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -17,19 +17,29 @@ CConfig::~CConfig() // do nothing } -void CConfig::addScreen(const CString& name) +bool CConfig::addScreen(const CString& name) { - if (m_map.count(name) != 0) { - assert(0 && "name already in map"); // FIXME -- throw instead + // alias name must not exist + if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) { + return false; } + + // add cell m_map.insert(std::make_pair(name, CCell())); + + // add name + m_nameToCanonicalName.insert(std::make_pair(name, name)); + + return true; } void CConfig::removeScreen(const CString& name) { - CCellMap::iterator index = m_map.find(name); + // get canonical name and find cell + CString canonical = getCanonicalName(name); + CCellMap::iterator index = m_map.find(canonical); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return; } // remove from map @@ -39,42 +49,111 @@ void CConfig::removeScreen(const CString& name) for (index = m_map.begin(); index != m_map.end(); ++index) { CCell& cell = index->second; for (SInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) - if (cell.m_neighbor[i] == name) { + if (getCanonicalName(cell.m_neighbor[i]) == canonical) { cell.m_neighbor[i].erase(); } } + + // remove aliases (and canonical name) + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ) { + if (index->second == canonical) { + m_nameToCanonicalName.erase(index++); + } + else { + ++index; + } + } } void CConfig::removeAllScreens() { m_map.clear(); + m_nameToCanonicalName.clear(); } -void CConfig::connect(const CString& srcName, +bool CConfig::addAlias(const CString& canonical, + const CString& alias) +{ + // alias name must not exist + if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) { + return false; + } + + // canonical name must be known + if (m_nameToCanonicalName.find(canonical) == m_nameToCanonicalName.end()) { + return false; + } + + // insert alias + m_nameToCanonicalName.insert(std::make_pair(alias, canonical)); + + return true; +} + +bool CConfig::removeAlias(const CString& alias) +{ + // must not be a canonical name + if (m_map.find(alias) != m_map.end()) { + return false; + } + + // find alias + CNameMap::iterator index = m_nameToCanonicalName.find(alias); + if (index == m_nameToCanonicalName.end()) { + return false; + } + + // remove alias + m_nameToCanonicalName.erase(index); + + return true; +} + +void CConfig::removeAllAliases() +{ + // remove all names + m_nameToCanonicalName.clear(); + + // put the canonical names back in + for (CCellMap::iterator index = m_map.begin(); + index != m_map.end(); ++index) { + m_nameToCanonicalName.insert( + std::make_pair(index->first, index->first)); + } +} + +bool CConfig::connect(const CString& srcName, EDirection srcSide, const CString& dstName) { // find source cell - CCellMap::iterator index = m_map.find(srcName); + CCellMap::iterator index = m_map.find(getCanonicalName(srcName)); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return false; } - // connect side (overriding any previous connection) + // connect side (overriding any previous connection). we + // canonicalize in getNeighbor() instead of here because the + // destination name doesn't have to exist yet. index->second.m_neighbor[srcSide - kFirstDirection] = dstName; + + return true; } -void CConfig::disconnect(const CString& srcName, +bool CConfig::disconnect(const CString& srcName, EDirection srcSide) { // find source cell CCellMap::iterator index = m_map.find(srcName); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return false; } // disconnect side index->second.m_neighbor[srcSide - kFirstDirection].erase(); + + return true; } bool CConfig::isValidScreenName(const CString& name) const @@ -133,20 +212,37 @@ CConfig::const_iterator CConfig::end() const bool CConfig::isScreen(const CString& name) const { - return (m_map.count(name) > 0); + return (m_nameToCanonicalName.count(name) > 0); +} + +bool CConfig::isCanonicalName(const CString& name) const +{ + return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); +} + +CString CConfig::getCanonicalName(const CString& name) const +{ + CNameMap::const_iterator index = m_nameToCanonicalName.find(name); + if (index == m_nameToCanonicalName.end()) { + return CString(); + } + else { + return index->second; + } } CString CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const { // find source cell - CCellMap::const_iterator index = m_map.find(srcName); + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return CString(); } // return connection - return index->second.m_neighbor[srcSide - kFirstDirection]; + return getCanonicalName(index->second.m_neighbor[ + srcSide - kFirstDirection]); } const char* CConfig::dirName(EDirection dir) @@ -183,6 +279,7 @@ void CConfig::readSection(std::istream& s) static const char s_section[] = "section:"; static const char s_screens[] = "screens"; static const char s_links[] = "links"; + static const char s_aliases[] = "aliases"; CString line; if (!readLine(s, line)) { @@ -213,6 +310,9 @@ void CConfig::readSection(std::istream& s) else if (name == s_links) { readSectionLinks(s); } + else if (name == s_aliases) { + readSectionAliases(s); + } else { throw XConfigRead("unknown section name"); } @@ -239,7 +339,9 @@ void CConfig::readSectionScreens(std::istream& s) } // add the screen to the configuration - addScreen(name); + if (!addScreen(name)) { + throw XConfigRead("duplicate screen name"); + } } else if (name.empty()) { throw XConfigRead("argument before first screen"); @@ -266,10 +368,13 @@ void CConfig::readSectionLinks(std::istream& s) // strip : screen = line.substr(0, line.size() - 1); - // verify we known about the screen + // verify we know about the screen if (!isScreen(screen)) { throw XConfigRead("unknown screen name"); } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } } else if (screen.empty()) { throw XConfigRead("argument before first screen"); @@ -328,6 +433,47 @@ void CConfig::readSectionLinks(std::istream& s) throw XConfigRead("unexpected end of links section"); } +void CConfig::readSectionAliases(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead("unknown screen name"); + } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // verify validity of screen name + if (!isValidScreenName(line)) { + throw XConfigRead("invalid screen alias"); + } + + // add alias + if (!addAlias(screen, line)) { + throw XConfigRead("alias is duplicate screen name"); + } + } + } + throw XConfigRead("unexpected end of aliases section"); +} + // // CConfig I/O @@ -384,6 +530,33 @@ std::ostream& operator<<(std::ostream& s, const CConfig& config) } s << "end" << std::endl; + // aliases section (if there are any) + if (config.m_map.size() != config.m_nameToCanonicalName.size()) { + // map canonical to alias + CConfig::CNameMap aliases; + for (CConfig::CNameMap::const_iterator + index = config.m_nameToCanonicalName.begin(); + index != config.m_nameToCanonicalName.end(); + ++index) { + if (index->first != index->second) { + aliases.insert(std::make_pair(index->second, index->first)); + } + } + + // dump it + CString screen; + s << "section: aliases" << std::endl; + for (CConfig::CNameMap::const_iterator index = aliases.begin(); + index != aliases.end(); ++index) { + if (index->first != screen) { + screen = index->first; + s << "\t" << screen.c_str() << std::endl; + } + s << "\t\t" << index->second.c_str() << std::endl; + } + s << "end" << std::endl; + } + return s; } diff --git a/server/CConfig.h b/server/CConfig.h index 22016182..f97c7a0a 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -6,6 +6,7 @@ #include "XBase.h" #include #include "stdmap.h" +#include "stdset.h" class CConfig; @@ -66,18 +67,33 @@ public: // manipulators // note that case is preserved in screen names but is ignored when - // comparing names. + // comparing names. screen names and their aliases share a + // namespace and must be unique. - // add/remove screens - void addScreen(const CString& name); + // add/remove screens. addScreen() returns false if the name + // already exists. the remove methods automatically remove + // aliases for the named screen and disconnect any connections + // to the removed screen(s). + bool addScreen(const CString& name); void removeScreen(const CString& name); void removeAllScreens(); - // connect edges - void connect(const CString& srcName, + // add/remove alias for a screen name. an alias can be used + // any place the canonical screen name can (except addScreen). + // addAlias() returns false if the alias name already exists + // or the canonical name is unknown. removeAlias() fails if + // the alias is unknown or a canonical name. + bool addAlias(const CString& canonical, + const CString& alias); + bool removeAlias(const CString& alias); + void removeAllAliases(); + + // connect/disconnect edges. both return false if srcName is + // unknown. + bool connect(const CString& srcName, EDirection srcSide, const CString& dstName); - void disconnect(const CString& srcName, + bool disconnect(const CString& srcName, EDirection srcSide); // accessors @@ -85,15 +101,23 @@ public: // returns true iff the given name is a valid screen name. bool isValidScreenName(const CString&) const; - // iterators over screen names + // iterators over (canonical) screen names const_iterator begin() const; const_iterator end() const; // returns true iff name names a screen bool isScreen(const CString& name) const; + // returns true iff name is the canonical name of a screen + bool isCanonicalName(const CString& name) const; + + // returns the canonical name of a screen or the empty string if + // the name is unknown. returns the canonical name if one is given. + CString getCanonicalName(const CString& name) const; + // get the neighbor in the given direction. returns the empty string - // if there is no neighbor in that direction. + // if there is no neighbor in that direction. returns the canonical + // screen name. CString getNeighbor(const CString&, EDirection) const; // read/write a configuration. operator>> will throw XConfigRead @@ -109,9 +133,13 @@ private: void readSection(std::istream&); void readSectionScreens(std::istream&); void readSectionLinks(std::istream&); + void readSectionAliases(std::istream&); private: + typedef std::map CNameMap; + CCellMap m_map; + CNameMap m_nameToCanonicalName; }; class XConfigRead : public XBase { diff --git a/server/CServer.cpp b/server/CServer.cpp index 578d4dfa..85e65179 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -171,38 +171,31 @@ bool CServer::setConfig(const CConfig& config) } // get the set of screens that are connected but are being - // dropped from the configuration. don't add the primary - // screen. also tell the secondary screen to disconnect. + // dropped from the configuration (or who's canonical name + // is changing). don't add the primary screen. also tell + // the secondary screen to disconnect. for (CScreenList::const_iterator index = m_screens.begin(); index != m_screens.end(); ++index) { - if (!config.isScreen(index->first) && - index->second != m_primaryInfo) { + if (index->second != m_primaryInfo && + !config.isCanonicalName(index->first)) { assert(index->second->m_protocol != NULL); index->second->m_protocol->sendClose(); threads.push_back(index->second->m_thread); } } + } - // wait a moment to allow each secondary screen to close - // its connection before we close it (to avoid having our - // socket enter TIME_WAIT). - if (threads.size() > 0) { - CThread::sleep(1.0); - } + // wait a moment to allow each secondary screen to close + // its connection before we close it (to avoid having our + // socket enter TIME_WAIT). + if (threads.size() > 0) { + CThread::sleep(1.0); + } - // cancel the old secondary screen threads - for (CThreads::iterator index = threads.begin(); + // cancel the old secondary screen threads + for (CThreads::iterator index = threads.begin(); index != threads.end(); ++index) { - index->cancel(); - } - - // cut over - m_config = config; - - // tell primary screen about reconfiguration - if (m_primary != NULL) { - m_primary->onConfigure(); - } + index->cancel(); } // wait for old secondary screen threads to disconnect. must @@ -213,6 +206,15 @@ bool CServer::setConfig(const CConfig& config) index->wait(); } + // cut over + CLock lock(&m_mutex); + m_config = config; + + // tell primary screen about reconfiguration + if (m_primary != NULL) { + m_primary->onConfigure(); + } + return true; } @@ -280,7 +282,8 @@ void CServer::setInfoNoLock(const CString& screen, assert(zoneSize >= 0); // screen must be connected - CScreenList::iterator index = m_screens.find(screen); + CString screenName = m_config.getCanonicalName(screen); + CScreenList::iterator index = m_screens.find(screenName); if (index == m_screens.end()) { throw XBadClient(); } @@ -339,14 +342,15 @@ void CServer::grabClipboardNoLock( CClipboardInfo& clipboard = m_clipboards[id]; // screen must be connected - CScreenList::iterator index = m_screens.find(screen); + CString screenName = m_config.getCanonicalName(screen); + CScreenList::iterator index = m_screens.find(screenName); if (index == m_screens.end()) { throw XBadClient(); } // ignore grab if sequence number is old. always allow primary // screen to grab. - if (screen != m_primaryInfo->m_name && + if (screenName != m_primaryInfo->m_name && seqNum < clipboard.m_clipboardSeqNum) { log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", screen.c_str(), id)); return; @@ -354,7 +358,7 @@ void CServer::grabClipboardNoLock( // mark screen as owning clipboard log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); - clipboard.m_clipboardOwner = screen; + clipboard.m_clipboardOwner = screenName; clipboard.m_clipboardSeqNum = seqNum; // no screens have the new clipboard except the sender @@ -363,7 +367,7 @@ void CServer::grabClipboardNoLock( // tell all other screens to take ownership of clipboard for (index = m_screens.begin(); index != m_screens.end(); ++index) { - if (index->first != screen) { + if (index->first != screenName) { CScreenInfo* info = index->second; if (info->m_protocol == NULL) { m_primary->grabClipboard(id); @@ -1460,19 +1464,21 @@ CServer::CScreenInfo* CServer::addConnection( CLock lock(&m_mutex); - // can only have one screen with a given name at any given time - if (m_screens.count(name) != 0) { - throw XDuplicateClient(name); - } - // name must be in our configuration if (!m_config.isScreen(name)) { throw XUnknownClient(name); } + CString screenName = m_config.getCanonicalName(name); + + // can only have one screen with a given name at any given time + if (m_screens.count(screenName) != 0) { + throw XDuplicateClient(name); + } // save screen info - CScreenInfo* newScreen = new CScreenInfo(name, protocol); - m_screens.insert(std::make_pair(name, newScreen)); + CScreenInfo* newScreen = new CScreenInfo(screenName, protocol); + m_screens.insert(std::make_pair(screenName, newScreen)); + log((CLOG_DEBUG "added connection \"%s\" (\"%s\")", name.c_str(), screenName.c_str())); return newScreen; } @@ -1483,7 +1489,8 @@ void CServer::removeConnection(const CString& name) CLock lock(&m_mutex); // find screen info - CScreenList::iterator index = m_screens.find(name); + CString screenName = m_config.getCanonicalName(name); + CScreenList::iterator index = m_screens.find(screenName); assert(index != m_screens.end()); // if this is active screen then we have to jump off of it diff --git a/server/server.cpp b/server/server.cpp index e33c3383..2a54cb03 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -394,14 +394,15 @@ static bool loadConfig(const char* pathname, bool require) log((CLOG_DEBUG "configuration read successfully")); return true; } - catch (XConfigRead&) { + catch (XConfigRead& e) { if (require) { - log((CLOG_PRINT "%s: cannot read configuration '%s'", - pname, pathname)); + log((CLOG_PRINT "%s: cannot read configuration '%s': %s", + pname, pathname, e.what())); bye(3); } else { - log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname)); + log((CLOG_DEBUG "cannot read configuration \"%s\": %s", + pathname, e.what())); } } return false;