diff --git a/PORTING b/PORTING index 55fc63c5..95aad05d 100644 --- a/PORTING +++ b/PORTING @@ -10,16 +10,19 @@ The synergy source code organization is: . -- root makefiles, some standard documentation cmd -- program source code - synergy -- synergy client - synergyd -- synergy server + launcher -- synergy launcher for Windows + synergyc -- synergy client + synergys -- synergy server config -- stuff for autoconf/automake dist -- files for creating distributions rpm -- files for creating RPMs doc -- placeholder for documentation examples -- example files lib -- library source code + arch -- platform dependent utility library base -- simple utilities client -- synergy client library + common -- commonly needed header files http -- simple http tools io -- I/O mt -- multithreading @@ -130,7 +133,7 @@ following these guidelines. * Other headers in directory, sorted alphabetically * Headers for each library, sorted alphabetically per library Include headers from the library closest in the dependency graph - first, then the next farthest, etc. sort alphabetically within + first, then the next farthest, etc. Sort alphabetically within each library. * System headers @@ -181,12 +184,8 @@ following these guidelines. Inheriting implementation from multiple classes can have unpleasant consequences in C++ due to it's limited capabilities. Classes can inherit from any number of interface classes. An interface class - provides only pure virtual methods. - - Synergy breaks this rule in two places. First is that IInterface - implements the virtual destructor. However, it's just an empty - method so it doesn't really count. Second is MXErrno which provides - implementation for exceptions that use the global errno variable. + provides only pure virtual methods. Synergy breaks this rule in + IInterface which implements the virtual destructor for convenience. * No globals Avoid global variables. All global variables must be static, making @@ -272,18 +271,20 @@ following these guidelines. * Open brace for function is on a line by itself in first column * Close brace for function lines up with open brace * Always use braces on: if, else, for, while, do, switch - * `else {' goes on it's own line + * `else {' goes on its own line * Always explicitly test pointers against NULL e.g. `if (ptr == NULL)' not `if (ptr)' + * Always explicitly test integral values against 0 + e.g. `if (i == 0)' not `if (i)' * Put spaces around binary operators and after statements e.g. `if (a == b) {' not `if(a==b){' * C'tor initializers are one per line, indented one tab stop * Other indentation should follow existing practice - * Use Qt style comments for extraction by doxygen + * Use Qt style comments for extraction by doxygen (i.e. //! and /*!) * Mark incomplete or buggy code with `FIXME' - Other - * calls to log() should always be all on one line (even past column 80) + * calls to LOG() should always be all on one line (even past column 80) Class Relationships @@ -300,4 +301,51 @@ FIXME -- high level overview of class relationships Portability ----------- -FIXME -- information about porting to new platforms +Porting synergy to a new platform requires the following steps: + +- Setting up the build +- Adjusting lib/common/common.h +- Implementing lib/arch +- Implementing lib/platform +- Implementing primary and secondary screens for the platform +- Tweaking synergyc and synergys + +Setting up the build: + +The first phase is simply to create the files necessary to build the +other files. On Unix, synergy uses autoconf/automake which produces +a `configure' script that generates makefiles. On Windows, synergy +uses Visual C++ workspace and project files. If you're porting to +another Unix variant, you may need to adjust `configure.in', +`acinclude.m4', and Unix flavor dependent code in lib/arch. + +Adjusting lib/common/common.h: + +The lib/common/common.h header file is included directly or indirectly +by every other file. It prepares some platform dependent macros for +integer sizes and defines a macro for conveniently testing which +platform we're building on. That macro is named *_LIKE (e.g. UNIX_LIKE) +and has the value `1'. Exactly one *_LIKE macro must be defined by +common.h. + +Implementing lib/arch: + +Most platform dependent code lives in lib/arch. There are several +interface classes there and they must all be implemented for each +platform. See the interface header files for more information. + +Implementing lib/platform: + +Most of the remaining platform dependent code lives in lib/platform. +The code there implements platform dependent window, clipboard, and +screen saver handling. The following interfaces must be implemented: + +FIXME + +Implementing primary and secondary screens for the platform: + +FIXME + +Tweaking synergyc and synergys: + +FIXME diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index f13345e7..13910634 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -12,8 +12,11 @@ * GNU General Public License for more details. */ -#include "CPlatform.h" #include "CLog.h" +#include "ILogOutputter.h" +#include "CArch.h" +#include "CStringUtil.h" +#include "XArch.h" #include "CAutoStart.h" #include "LaunchUtil.h" #include "resource.h" @@ -23,6 +26,37 @@ #define CLIENT_DAEMON_INFO "Shares this system's mouse and keyboard with others." #define SERVER_DAEMON_INFO "Shares this system's mouse and keyboard with others." +// +// CAutoStartOutputter +// +// This class detects a message above a certain level and saves it +// + +class CAutoStartOutputter : public ILogOutputter { +public: + CAutoStartOutputter(CString* msg) : m_msg(msg) { } + virtual ~CAutoStartOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } + +private: + CString* m_msg; +}; + +bool +CAutoStartOutputter::write(ELevel level, const char* message) +{ + if (level <= CLog::kERROR) { + *m_msg = message; + } + return false; +} + + // // CAutoStart // @@ -51,8 +85,7 @@ void CAutoStart::doModal() { // install our log outputter - CLog::Outputter oldOutputter = CLog::getOutputter(); - CLog::setOutputter(&CAutoStart::onLog); + CLOG->insert(new CAutoStartOutputter(&m_errorMessage)); // reset saved flag m_userConfigSaved = false; @@ -61,8 +94,8 @@ CAutoStart::doModal() DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART), m_parent, dlgProc, (LPARAM)this); - // restore log outputter - CLog::setOutputter(oldOutputter); + // remove log outputter + CLOG->pop_front(); } bool @@ -74,19 +107,16 @@ CAutoStart::wasUserConfigSaved() const void CAutoStart::update() { - // need a platform object - CPlatform platform; - // get installation state - const bool installedSystem = platform.isDaemonInstalled( + const bool installedSystem = ARCH->isDaemonInstalled( m_name.c_str(), true); - const bool installedUser = platform.isDaemonInstalled( + const bool installedUser = ARCH->isDaemonInstalled( m_name.c_str(), false); // get user's permissions - const bool canInstallSystem = platform.canInstallDaemon( + const bool canInstallSystem = ARCH->canInstallDaemon( m_name.c_str(), true); - const bool canInstallUser = platform.canInstallDaemon( + const bool canInstallUser = ARCH->canInstallDaemon( m_name.c_str(), false); // update messages @@ -165,22 +195,25 @@ CAutoStart::onInstall(bool allUsers) m_errorMessage = ""; // install - CPlatform platform; - if (!platform.installDaemon(m_name.c_str(), + try { + ARCH->installDaemon(m_name.c_str(), m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, - appPath.c_str(), m_cmdLine.c_str(), allUsers)) { + appPath.c_str(), m_cmdLine.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), + getString(allUsers ? + IDS_INSTALLED_SYSTEM : + IDS_INSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { if (m_errorMessage.empty()) { - m_errorMessage = getString(IDS_INSTALL_GENERIC_ERROR); + m_errorMessage = CStringUtil::format( + getString(IDS_INSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); } showError(m_hwnd, m_errorMessage); return false; } - - askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), - getString(allUsers ? - IDS_INSTALLED_SYSTEM : - IDS_INSTALLED_USER)); - return true; } bool @@ -190,30 +223,23 @@ CAutoStart::onUninstall(bool allUsers) m_errorMessage = ""; // uninstall - CPlatform platform; - if (platform.uninstallDaemon(m_name.c_str(), allUsers) != - IPlatform::kSuccess) { + try { + ARCH->uninstallDaemon(m_name.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE), + getString(allUsers ? + IDS_UNINSTALLED_SYSTEM : + IDS_UNINSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { if (m_errorMessage.empty()) { - m_errorMessage = getString(IDS_UNINSTALL_GENERIC_ERROR); + m_errorMessage = CStringUtil::format( + getString(IDS_UNINSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); } showError(m_hwnd, m_errorMessage); return false; } - - askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE), - getString(allUsers ? - IDS_UNINSTALLED_SYSTEM : - IDS_UNINSTALLED_USER)); - return true; -} - -bool -CAutoStart::onLog(int priority, const char* message) -{ - if (priority <= CLog::kERROR) { - s_singleton->m_errorMessage = message; - } - return true; } BOOL diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h index 954801ab..e931530e 100644 --- a/cmd/launcher/CAutoStart.h +++ b/cmd/launcher/CAutoStart.h @@ -57,9 +57,6 @@ private: bool onInstall(bool allUsers); bool onUninstall(bool allUsers); - // log handling - static bool onLog(int priority, const char* message); - // message handling BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index 57e31554..ccd35c07 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -13,8 +13,8 @@ */ #include "CConfig.h" -#include "CPlatform.h" #include "LaunchUtil.h" +#include "CArch.h" #include "resource.h" #include "stdfstream.h" @@ -107,10 +107,9 @@ CString getAppPath(const CString& appName) { // prepare path to app - CPlatform platform; char myPathname[MAX_PATH]; GetModuleFileName(s_instance, myPathname, MAX_PATH); - const char* myBasename = platform.getBasename(myPathname); + const char* myBasename = ARCH->getBasename(myPathname); CString appPath = CString(myPathname, myBasename - myPathname); appPath += appName; return appPath; @@ -136,24 +135,20 @@ loadConfig(const CString& pathname, CConfig& config) bool loadConfig(CConfig& config) { - CPlatform platform; - // load configuration bool configLoaded = false; - CString path = platform.getUserDirectory(); + CString path = ARCH->getUserDirectory(); if (!path.empty()) { - CPlatform platform; - // try loading the user's configuration - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; } else { // try the system-wide config file - path = platform.getSystemDirectory(); + path = ARCH->getSystemDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; } @@ -183,13 +178,11 @@ saveConfig(const CString& pathname, const CConfig& config) bool saveConfig(const CConfig& config, bool sysOnly) { - CPlatform platform; - // try saving the user's configuration if (!sysOnly) { - CString path = platform.getUserDirectory(); + CString path = ARCH->getUserDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { return true; } @@ -198,9 +191,9 @@ saveConfig(const CConfig& config, bool sysOnly) // try the system-wide config file else { - CString path = platform.getSystemDirectory(); + CString path = ARCH->getSystemDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { return true; } diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 603354fd..0b17b224 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -14,9 +14,9 @@ #include "CConfig.h" #include "ProtocolTypes.h" -#include "CPlatform.h" -#include "CNetwork.h" #include "CLog.h" +#include "CStringUtil.h" +#include "CArch.h" #include "Version.h" #include "stdvector.h" #include "resource.h" @@ -62,10 +62,26 @@ static const char* s_debugName[][2] = { }; static const int s_defaultDebug = 3; // INFO -static HWND s_mainWindow; -static CConfig s_config; -static CConfig s_oldConfig; -static CStringList s_screens; +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + CConfig m_config; + CConfig m_oldConfig; + CStringList m_screens; +}; + +CArgs* CArgs::s_instance = NULL; + static BOOL CALLBACK @@ -117,7 +133,7 @@ static void enableSaveControls(HWND hwnd) { - enableItem(hwnd, IDC_MAIN_SAVE, s_config != s_oldConfig); + enableItem(hwnd, IDC_MAIN_SAVE, ARG->m_config != ARG->m_oldConfig); } static @@ -171,8 +187,8 @@ updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) // add all screens to combo box if (!screen.empty()) { - for (CConfig::const_iterator index = s_config.begin(); - index != s_config.end(); ++index) { + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++index) { SendMessage(hwnd, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)index->c_str()); } @@ -184,7 +200,7 @@ updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) // select neighbor in combo box LRESULT index = 0; if (!screen.empty()) { - const CString& neighbor = s_config.getNeighbor(screen, direction); + const CString& neighbor = ARG->m_config.getNeighbor(screen, direction); if (!neighbor.empty()) { index = SendMessage(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)neighbor.c_str()); @@ -205,7 +221,7 @@ updateNeighbors(HWND hwnd) HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); if (index != LB_ERR) { - screen = s_screens[index]; + screen = ARG->m_screens[index]; } // set neighbor combo boxes @@ -230,7 +246,7 @@ addScreen(HWND hwnd) if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), hwnd, addDlgProc, (LPARAM)&info) != 0) { // get current number of screens - UInt32 i = s_screens.size(); + UInt32 i = ARG->m_screens.size(); // add screen to list control HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); @@ -239,15 +255,15 @@ addScreen(HWND hwnd) SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); // add screen to screen list - s_screens.push_back(info.m_screen); + ARG->m_screens.push_back(info.m_screen); // add screen to config - s_config.addScreen(info.m_screen); + ARG->m_config.addScreen(info.m_screen); // add aliases to config for (CStringList::const_iterator index = info.m_aliases.begin(); index != info.m_aliases.end(); ++index) { - s_config.addAlias(info.m_screen, *index); + ARG->m_config.addAlias(info.m_screen, *index); } // update neighbors @@ -271,9 +287,9 @@ editScreen(HWND hwnd) // fill in screen info CScreenInfo info; - info.m_screen = s_screens[index]; - for (CConfig::all_const_iterator index = s_config.beginAll(); - index != s_config.endAll(); ++index) { + info.m_screen = ARG->m_screens[index]; + for (CConfig::all_const_iterator index = ARG->m_config.beginAll(); + index != ARG->m_config.endAll(); ++index) { if (CStringUtil::CaselessCmp::equal(index->second, info.m_screen) && !CStringUtil::CaselessCmp::equal(index->second, index->first)) { info.m_aliases.push_back(index->first); @@ -287,21 +303,21 @@ editScreen(HWND hwnd) if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), hwnd, addDlgProc, (LPARAM)&info) != 0) { // replace screen - s_screens[index] = info.m_screen; + ARG->m_screens[index] = info.m_screen; // remove old aliases for (CStringList::const_iterator index = oldInfo.m_aliases.begin(); index != oldInfo.m_aliases.end(); ++index) { - s_config.removeAlias(*index); + ARG->m_config.removeAlias(*index); } // replace name - s_config.renameScreen(oldInfo.m_screen, info.m_screen); + ARG->m_config.renameScreen(oldInfo.m_screen, info.m_screen); // add new aliases for (CStringList::const_iterator index = info.m_aliases.begin(); index != info.m_aliases.end(); ++index) { - s_config.addAlias(info.m_screen, *index); + ARG->m_config.addAlias(info.m_screen, *index); } // update list @@ -331,16 +347,16 @@ removeScreen(HWND hwnd) } // get screen name - CString name = s_screens[index]; + CString name = ARG->m_screens[index]; // remove screen from list control SendMessage(child, LB_DELETESTRING, index, 0); // remove screen from screen list - s_screens.erase(&s_screens[index]); + ARG->m_screens.erase(&ARG->m_screens[index]); // remove screen from config (this also removes aliases) - s_config.removeScreen(name); + ARG->m_config.removeScreen(name); // update neighbors updateNeighbors(hwnd); @@ -361,20 +377,20 @@ changeNeighbor(HWND hwnd, HWND combo, EDirection direction) } // get screen name - CString screen = s_screens[index]; + CString screen = ARG->m_screens[index]; // get selected neighbor index = SendMessage(combo, CB_GETCURSEL, 0, 0); // remove old connection - s_config.disconnect(screen, direction); + ARG->m_config.disconnect(screen, direction); // add new connection if (index != LB_ERR && index != 0) { LRESULT size = SendMessage(combo, CB_GETLBTEXTLEN, index, 0); char* neighbor = new char[size + 1]; SendMessage(combo, CB_GETLBTEXT, index, (LPARAM)neighbor); - s_config.connect(screen, direction, CString(neighbor)); + ARG->m_config.connect(screen, direction, CString(neighbor)); delete[] neighbor; } @@ -444,14 +460,14 @@ getCommandLine(HWND hwnd, bool testing) // get and verify screen name HWND child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); CString name = getWindowText(child); - if (!s_config.isValidScreenName(name)) { + if (!ARG->m_config.isValidScreenName(name)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SCREEN_NAME).c_str(), name.c_str())); SetFocus(child); return CString(); } - if (!isClient && !s_config.isScreen(name)) { + if (!isClient && !ARG->m_config.isScreen(name)) { showError(hwnd, CStringUtil::format( getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), name.c_str())); @@ -490,7 +506,7 @@ getCommandLine(HWND hwnd, bool testing) // check server name child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); CString server = getWindowText(child); - if (!s_config.isValidScreenName(server)) { + if (!ARG->m_config.isValidScreenName(server)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SERVER_NAME).c_str(), server.c_str())); @@ -649,15 +665,13 @@ static void initMainWindow(HWND hwnd) { - CPlatform platform; - // append version number to title CString titleFormat = getString(IDS_TITLE); setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION)); // load configuration - bool configLoaded = loadConfig(s_config); - s_oldConfig = s_config; + bool configLoaded = loadConfig(ARG->m_config); + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); // choose client/server radio buttons @@ -673,9 +687,9 @@ initMainWindow(HWND hwnd) if (configLoaded) { int i = 1; child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - for (CConfig::const_iterator index = s_config.begin(); - index != s_config.end(); ++i, ++index) { - s_screens.push_back(*index); + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++i, ++index) { + ARG->m_screens.push_back(*index); CString item = CStringUtil::print("%d. %s", i, index->c_str()); SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); } @@ -687,9 +701,9 @@ initMainWindow(HWND hwnd) child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); - CNetwork::gethostname(buffer, sizeof(buffer)); + CString hostname = ARCH->getHostName(); child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); - SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)hostname.c_str()); child = getItem(hwnd, IDC_MAIN_DEBUG); for (unsigned int i = 0; i < sizeof(s_debugName) / @@ -746,7 +760,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) tokenize(newAliases, getWindowText(child)); // name must be valid - if (!s_config.isValidScreenName(newName)) { + if (!ARG->m_config.isValidScreenName(newName)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SCREEN_NAME).c_str(), newName.c_str())); @@ -756,7 +770,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // aliases must be valid for (CStringList::const_iterator index = newAliases.begin(); index != newAliases.end(); ++index) { - if (!s_config.isValidScreenName(*index)) { + if (!ARG->m_config.isValidScreenName(*index)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SCREEN_NAME).c_str(), index->c_str())); @@ -775,7 +789,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // name must not exist in config but allow same name. also // allow name if it exists in the old alias list but not the // new one. - if (s_config.isScreen(newName) && + if (ARG->m_config.isScreen(newName) && !CStringUtil::CaselessCmp::equal(newName, info->m_screen) && !isNameInList(info->m_aliases, newName)) { showError(hwnd, CStringUtil::format( @@ -788,7 +802,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // allow an alias to be the old name. for (CStringList::const_iterator index = newAliases.begin(); index != newAliases.end(); ++index) { - if (s_config.isScreen(*index) && + if (ARG->m_config.isScreen(*index) && !CStringUtil::CaselessCmp::equal(*index, info->m_screen) && !isNameInList(info->m_aliases, *index)) { showError(hwnd, CStringUtil::format( @@ -830,7 +844,7 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) switch (LOWORD(wParam)) { case IDCANCEL: // test for unsaved data - if (s_config != s_oldConfig) { + if (ARG->m_config != ARG->m_oldConfig) { if (!askVerify(hwnd, getString(IDS_UNSAVED_DATA_REALLY_QUIT))) { return 0; } @@ -846,14 +860,14 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST); // save data - if (s_config != s_oldConfig) { - if (!saveConfig(s_config, false)) { + if (ARG->m_config != ARG->m_oldConfig) { + if (!saveConfig(ARG->m_config, false)) { showError(hwnd, CStringUtil::format( getString(IDS_SAVE_FAILED).c_str(), getErrorString(GetLastError()).c_str())); return 0; } - s_oldConfig = s_config; + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); } @@ -892,11 +906,11 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) if (!cmdLine.empty()) { // run dialog CAutoStart autoStart(hwnd, - isClientChecked(hwnd) ? NULL : &s_config, + isClientChecked(hwnd) ? NULL : &ARG->m_config, cmdLine); autoStart.doModal(); if (autoStart.wasUserConfigSaved()) { - s_oldConfig = s_config; + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); } } @@ -904,13 +918,13 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } case IDC_MAIN_SAVE: - if (!saveConfig(s_config, false)) { + if (!saveConfig(ARG->m_config, false)) { showError(hwnd, CStringUtil::format( getString(IDS_SAVE_FAILED).c_str(), getErrorString(GetLastError()).c_str())); } else { - s_oldConfig = s_config; + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); } return 0; @@ -981,10 +995,11 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) { - s_instance = instance; + CArch arch; + CLOG; + CArgs args; - // initialize network library - CNetwork::init(); + s_instance = instance; // register main window (dialog) class WNDCLASSEX classInfo; @@ -1009,13 +1024,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) RegisterClassEx(&classInfo); // create main window - s_mainWindow = CreateDialog(s_instance, MAKEINTRESOURCE(IDD_MAIN), 0, NULL); + HWND m_mainWindow = CreateDialog(s_instance, + MAKEINTRESOURCE(IDD_MAIN), 0, NULL); // prep window - initMainWindow(s_mainWindow); + initMainWindow(m_mainWindow); // show window - ShowWindow(s_mainWindow, nCmdShow); + ShowWindow(m_mainWindow, nCmdShow); // main loop MSG msg; @@ -1032,7 +1048,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) break; default: - if (!IsDialogMessage(s_mainWindow, &msg)) { + if (!IsDialogMessage(m_mainWindow, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index f93739aa..51096677 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index af23a29a..6091c59f 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -241,8 +241,8 @@ BEGIN "Synergy is not configured to start automatically." IDS_INSTALL_LABEL "Install" IDS_UNINSTALL_LABEL "Uninstall" - IDS_INSTALL_GENERIC_ERROR "Install failed for an unknown reason." - IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed for an unknown reason." + IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}." + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}." IDS_INSTALL_TITLE "Installed Auto-Start" IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 98ab2824..9af3e12c 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -38,6 +38,7 @@ synergyc_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ -lXtst \ @@ -46,6 +47,8 @@ synergyc_LDADD = \ $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 495c0b6f..a6c03c2a 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -14,11 +14,9 @@ #include "CClient.h" #include "ISecondaryScreenFactory.h" -#include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" -#include "CNetwork.h" #include "CNetworkAddress.h" #include "CTCPSocketFactory.h" #include "XSocket.h" @@ -28,12 +26,18 @@ #include "CThread.h" #include "XThread.h" #include "CLog.h" +#include "LogOutputters.h" #include "CString.h" +#include "CArch.h" +#include "CArchMiscWindows.h" #include +#define DAEMON_RUNNING(running_) #if WINDOWS_LIKE #include "CMSWindowsSecondaryScreen.h" #include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE #include "CXWindowsSecondaryScreen.h" #endif @@ -49,35 +53,33 @@ // program arguments // -static const char* pname = NULL; -static bool s_backend = false; -static bool s_restartable = true; -static bool s_daemon = true; -static bool s_camp = true; -static const char* s_logFilter = NULL; -static CString s_name; -static CNetworkAddress s_serverAddress; +#define ARG CArgs::s_instance +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_camp(true), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } -// -// logging thread safety -// +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + bool m_camp; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_serverAddress; +}; -static CMutex* s_logMutex = NULL; - -static -void -logLock(bool lock) -{ - assert(s_logMutex != NULL); - - if (lock) { - s_logMutex->lock(); - } - else { - s_logMutex->unlock(); - } -} +CArgs* CArgs::s_instance = NULL; // @@ -113,27 +115,17 @@ static CClient* s_client = NULL; static int -realMain(CMutex* mutex) +realMain(void) { - // caller should have mutex locked on entry - - // initialize threading library - CThread::init(); - - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - int result = kExitSuccess; do { bool opened = false; bool locked = true; try { // create client - s_client = new CClient(s_name); - s_client->camp(s_camp); - s_client->setAddress(s_serverAddress); + s_client = new CClient(ARG->m_name); + s_client->camp(ARG->m_camp); + s_client->setAddress(ARG->m_serverAddress); s_client->setScreenFactory(new CSecondaryScreenFactory); s_client->setSocketFactory(new CTCPSocketFactory); s_client->setStreamFilterFactory(NULL); @@ -144,9 +136,7 @@ realMain(CMutex* mutex) opened = true; // run client - if (mutex != NULL) { - mutex->unlock(); - } + DAEMON_RUNNING(true); locked = false; s_client->mainLoop(); locked = true; @@ -160,8 +150,8 @@ realMain(CMutex* mutex) // clean up #define FINALLY do { \ - if (!locked && mutex != NULL) { \ - mutex->lock(); \ + if (!locked) { \ + DAEMON_RUNNING(false); \ } \ if (s_client != NULL) { \ if (opened) { \ @@ -175,8 +165,8 @@ realMain(CMutex* mutex) } catch (XScreenUnavailable& e) { // wait before retrying if we're going to retry - if (s_restartable) { - CThread::sleep(e.getRetryTime()); + if (ARG->m_restartable) { + ARCH->sleep(e.getRetryTime()); } else { result = kExitFailed; @@ -189,8 +179,8 @@ realMain(CMutex* mutex) } catch (...) { // don't try to restart and fail - s_restartable = false; - result = kExitFailed; + ARG->m_restartable = false; + result = kExitFailed; FINALLY; } #undef FINALLY @@ -200,19 +190,10 @@ realMain(CMutex* mutex) } catch (XThread&) { // terminated - s_restartable = false; - result = kExitTerminated; + ARG->m_restartable = false; + result = kExitTerminated; } - catch (...) { - CLog::setLock(NULL); - s_logMutex = NULL; - throw; - } - } while (s_restartable); - - // clean up - CLog::setLock(NULL); - s_logMutex = NULL; + } while (ARG->m_restartable); return result; } @@ -233,7 +214,7 @@ version() LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n" "%s", - pname, + ARG->m_pname, kVersion, kProtocolMajorVersion, kProtocolMinorVersion, @@ -279,7 +260,7 @@ help() "\n" "Where log messages go depends on the platform and whether or not the\n" "client is running as a daemon.", - pname, kDefaultPort)); + ARG->m_pname, kDefaultPort)); } @@ -294,7 +275,7 @@ isArg(int argi, int argc, const char** argv, // match. check args left. if (argi + minRequiredParameters >= argc) { LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, - pname, argv[argi], pname)); + ARG->m_pname, argv[argi], ARG->m_pname)); bye(kExitArgs); } return true; @@ -308,61 +289,58 @@ static void parse(int argc, const char** argv) { - assert(pname != NULL); - assert(argv != NULL); - assert(argc >= 1); + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); // set defaults - char hostname[256]; - if (CNetwork::gethostname(hostname, sizeof(hostname)) != CNetwork::Error) { - s_name = hostname; - } + ARG->m_name = ARCH->getHostName(); // parse options int i; for (i = 1; i < argc; ++i) { if (isArg(i, argc, argv, "-d", "--debug", 1)) { // change logging level - s_logFilter = argv[++i]; + ARG->m_logFilter = argv[++i]; } else if (isArg(i, argc, argv, "-n", "--name", 1)) { // save screen name - s_name = argv[++i]; + ARG->m_name = argv[++i]; } else if (isArg(i, argc, argv, NULL, "--camp")) { // enable camping - s_camp = true; + ARG->m_camp = true; } else if (isArg(i, argc, argv, NULL, "--no-camp")) { // disable camping - s_camp = false; + ARG->m_camp = false; } else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon - s_daemon = false; + ARG->m_daemon = false; } else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize - s_daemon = true; + ARG->m_daemon = true; } else if (isArg(i, argc, argv, "-1", "--no-restart")) { // don't try to restart - s_restartable = false; + ARG->m_restartable = false; } else if (isArg(i, argc, argv, NULL, "--restart")) { // try to restart - s_restartable = true; + ARG->m_restartable = true; } else if (isArg(i, argc, argv, "-z", NULL)) { - s_backend = true; + ARG->m_backend = true; } else if (isArg(i, argc, argv, "-h", "--help")) { @@ -383,7 +361,7 @@ parse(int argc, const char** argv) else if (argv[i][0] == '-') { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } @@ -396,49 +374,60 @@ parse(int argc, const char** argv) // exactly one non-option argument (server-address) if (i == argc) { LOG((CLOG_PRINT "%s: a server address or name is required" BYE, - pname, pname)); + ARG->m_pname, ARG->m_pname)); bye(kExitArgs); } if (i + 1 != argc) { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } // save server address try { - s_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, - pname, e.what(), pname)); + ARG->m_pname, e.what(), ARG->m_pname)); bye(kExitFailed); } // increase default filter level for daemon. the user must // explicitly request another level for a daemon. - if (s_daemon && s_logFilter == NULL) { + if (ARG->m_daemon && ARG->m_logFilter == NULL) { #if WINDOWS_LIKE - if (CPlatform::isWindows95Family()) { + if (CArchMiscWindows::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. - s_logFilter = "FATAL"; + ARG->m_logFilter = "FATAL"; } else #endif { - s_logFilter = "NOTE"; + ARG->m_logFilter = "NOTE"; } } // set log filter - if (!CLog::setFilter(s_logFilter)) { + if (!CLOG->setFilter(ARG->m_logFilter)) { LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, - pname, s_logFilter, pname)); + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); bye(kExitArgs); } } +static +void +useSystemLog() +{ + // redirect log messages + ILogOutputter* logger = new CSystemLogOutputter; + logger->open(DAEMON_NAME); + CLOG->insert(new CStopLogOutputter); + CLOG->insert(logger); +} + // // platform dependent entry points // @@ -449,24 +438,41 @@ parse(int argc, const char** argv) static bool s_hasImportantLogMessages = false; -static +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + bool -logMessageBox(int priority, const char* msg) +CMessageBoxOutputter::write(ELevel level, const char* message) { // note any important messages the user may need to know about - if (priority <= CLog::kWARNING) { + if (level <= CLog::kWARNING) { s_hasImportantLogMessages = true; } // FATAL and PRINT messages get a dialog box if not running as // backend. if we're running as a backend the user will have // a chance to see the messages when we exit. - if (!s_backend && priority <= CLog::kFATAL) { - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); - return true; + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; } else { - return false; + return true; } } @@ -474,7 +480,7 @@ static void byeThrow(int x) { - throw CWin32Platform::CDaemonFailed(x); + CArchMiscWindows::daemonFailed(x); } static @@ -486,10 +492,9 @@ daemonStop(void) static int -daemonStartup(IPlatform* iplatform, int argc, const char** argv) +daemonStartup(int argc, const char** argv) { - // get platform pointer - CWin32Platform* platform = static_cast(iplatform); + useSystemLog(); // catch errors that would normally exit bye = &byeThrow; @@ -498,35 +503,35 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) parse(argc, argv); // cannot run as backend if running as a service - s_backend = false; + ARG->m_backend = false; // run as a service - return platform->runDaemon(realMain, daemonStop); + return CArchMiscWindows::runDaemon(realMain, daemonStop); } static int -daemonStartup95(IPlatform*, int, const char**) +daemonStartup95(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // save instance CMSWindowsScreen::init(instance); // get program name - pname = platform.getBasename(__argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(__argv[0]); // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); + CLOG->insert(new CMessageBoxOutputter); // windows NT family starts services using no command line options. // since i'm not sure how to tell the difference between that and @@ -534,10 +539,10 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // arguments and we're on NT then we're being invoked as a service. // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. - if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { - int result = platform.daemonize(DAEMON_NAME, &daemonStartup); + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + int result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); return kExitFailed; } return result; @@ -548,38 +553,36 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // daemonize if requested int result; - if (s_daemon) { - // redirect log messages - platform.installDaemonLogger(DAEMON_NAME); - + if (ARG->m_daemon) { // start as a daemon - if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); - if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + if (CArchMiscWindows::isWindows95Family()) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); result = kExitFailed; } } else { // cannot start a service from the command line so just // run normally (except with log messages redirected). - result = realMain(NULL); + useSystemLog(); + result = realMain(); } } else { // run - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. - if (s_backend && s_hasImportantLogMessages) { + if (ARG->m_backend && s_hasImportantLogMessages) { char msg[1024]; msg[0] = '\0'; LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); } return result; @@ -589,40 +592,40 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) static int -daemonStartup(IPlatform*, int, const char**) +daemonStartup(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int main(int argc, char** argv) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // get program name - pname = platform.getBasename(argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(argv[0]); // parse command line parse(argc, const_cast(argv)); // daemonize if requested int result; - if (s_daemon) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup); - if (result == -1) { + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { LOG((CLOG_CRIT "failed to daemonize")); - return kExitFailed; + result = kExitFailed; } } else { - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - return result; } diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp index ad4e1f1c..e99b0910 100644 --- a/cmd/synergyc/synergyc.dsp +++ b/cmd/synergyc/synergyc.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -54,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergyc.exe" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 !ELSEIF "$(CFG)" == "synergyc - Win32 Debug" @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -81,7 +81,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../Debug/synergyc.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept !ENDIF diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index 9a200c51..8da55b4b 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -39,6 +39,7 @@ synergys_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ -lXtst \ @@ -47,6 +48,8 @@ synergys_LDADD = \ $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 522f6710..edc09560 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -15,11 +15,9 @@ #include "CServer.h" #include "CConfig.h" #include "IPrimaryScreenFactory.h" -#include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" -#include "CNetwork.h" #include "CTCPSocketFactory.h" #include "XSocket.h" #include "CLock.h" @@ -27,12 +25,18 @@ #include "CThread.h" #include "XThread.h" #include "CLog.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "CArchMiscWindows.h" #include "stdfstream.h" #include +#define DAEMON_RUNNING(running_) #if WINDOWS_LIKE #include "CMSWindowsPrimaryScreen.h" #include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE #include "CXWindowsPrimaryScreen.h" #endif @@ -57,37 +61,35 @@ // program arguments // -static const char* pname = NULL; -static bool s_backend = false; -static bool s_restartable = true; -static bool s_daemon = true; -static const char* s_configFile = NULL; -static const char* s_logFilter = NULL; -static CString s_name; -static CNetworkAddress s_synergyAddress; -static CNetworkAddress s_httpAddress; -static CConfig s_config; +#define ARG CArgs::s_instance +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_configFile(NULL), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } -// -// logging thread safety -// +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + const char* m_configFile; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_synergyAddress; + CNetworkAddress m_httpAddress; + CConfig m_config; +}; -static CMutex* s_logMutex = NULL; - -static -void -logLock(bool lock) -{ - assert(s_logMutex != NULL); - - if (lock) { - s_logMutex->lock(); - } - else { - s_logMutex->unlock(); - } -} +CArgs* CArgs::s_instance = NULL; // @@ -124,18 +126,8 @@ static CServer* s_server = NULL; static int -realMain(CMutex* mutex) +realMain(void) { - // caller should have mutex locked on entry - - // initialize threading library - CThread::init(); - - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - int result = kExitSuccess; do { bool opened = false; @@ -143,28 +135,28 @@ realMain(CMutex* mutex) try { // if configuration has no screens then add this system // as the default - if (s_config.begin() == s_config.end()) { - s_config.addScreen(s_name); + if (ARG->m_config.begin() == ARG->m_config.end()) { + ARG->m_config.addScreen(ARG->m_name); } // set the contact address, if provided, in the config. // otherwise, if the config doesn't have an address, use // the default. - if (s_synergyAddress.isValid()) { - s_config.setSynergyAddress(s_synergyAddress); + if (ARG->m_synergyAddress.isValid()) { + ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); } - else if (!s_config.getSynergyAddress().isValid()) { - s_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + else if (!ARG->m_config.getSynergyAddress().isValid()) { + ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); } // set HTTP address if provided - if (s_httpAddress.isValid()) { - s_config.setHTTPAddress(s_httpAddress); + if (ARG->m_httpAddress.isValid()) { + ARG->m_config.setHTTPAddress(ARG->m_httpAddress); } // create server - s_server = new CServer(s_name); - s_server->setConfig(s_config); + s_server = new CServer(ARG->m_name); + s_server->setConfig(ARG->m_config); s_server->setScreenFactory(new CPrimaryScreenFactory); s_server->setSocketFactory(new CTCPSocketFactory); s_server->setStreamFilterFactory(NULL); @@ -174,18 +166,16 @@ realMain(CMutex* mutex) s_server->open(); opened = true; - // run server (unlocked) - if (mutex != NULL) { - mutex->unlock(); - } + // run server + DAEMON_RUNNING(true); locked = false; s_server->mainLoop(); locked = true; // clean up #define FINALLY do { \ - if (!locked && mutex != NULL) { \ - mutex->lock(); \ + if (!locked) { \ + DAEMON_RUNNING(false); \ } \ if (s_server != NULL) { \ if (opened) { \ @@ -199,8 +189,8 @@ realMain(CMutex* mutex) } catch (XScreenUnavailable& e) { // wait before retrying if we're going to retry - if (s_restartable) { - CThread::sleep(e.getRetryTime()); + if (ARG->m_restartable) { + ARCH->sleep(e.getRetryTime()); } else { result = kExitFailed; @@ -213,8 +203,8 @@ realMain(CMutex* mutex) } catch (...) { // don't try to restart and fail - s_restartable = false; - result = kExitFailed; + ARG->m_restartable = false; + result = kExitFailed; FINALLY; } #undef FINALLY @@ -224,19 +214,10 @@ realMain(CMutex* mutex) } catch (XThread&) { // terminated - s_restartable = false; - result = kExitTerminated; + ARG->m_restartable = false; + result = kExitTerminated; } - catch (...) { - CLog::setLock(NULL); - s_logMutex = NULL; - throw; - } - } while (s_restartable); - - // clean up - CLog::setLock(NULL); - s_logMutex = NULL; + } while (ARG->m_restartable); return result; } @@ -257,7 +238,7 @@ version() LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n" "%s", - pname, + ARG->m_pname, kVersion, kProtocolMajorVersion, kProtocolMinorVersion, @@ -287,8 +268,6 @@ help() #endif - CPlatform platform; - LOG((CLOG_PRINT "Usage: %s" " [--address
]" @@ -333,13 +312,13 @@ PLATFORM_EXTRA "\n" "Where log messages go depends on the platform and whether or not the\n" "server is running as a daemon.", - pname, + ARG->m_pname, kDefaultPort, - platform.addPathComponent( - platform.getUserDirectory(), + ARCH->concatPath( + ARCH->getUserDirectory(), USR_CONFIG_NAME).c_str(), - platform.addPathComponent( - platform.getSystemDirectory(), + ARCH->concatPath( + ARCH->getSystemDirectory(), SYS_CONFIG_NAME).c_str())); } @@ -354,7 +333,7 @@ isArg(int argi, int argc, const char** argv, // match. check args left. if (argi + minRequiredParameters >= argc) { LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, - pname, argv[argi], pname)); + ARG->m_pname, argv[argi], ARG->m_pname)); bye(kExitArgs); } return true; @@ -368,32 +347,30 @@ static void parse(int argc, const char** argv) { - assert(pname != NULL); - assert(argv != NULL); - assert(argc >= 1); + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); // set defaults - char hostname[256]; - if (CNetwork::gethostname(hostname, sizeof(hostname)) != CNetwork::Error) { - s_name = hostname; - } + ARG->m_name = ARCH->getHostName(); // parse options int i; for (i = 1; i < argc; ++i) { if (isArg(i, argc, argv, "-d", "--debug", 1)) { // change logging level - s_logFilter = argv[++i]; + ARG->m_logFilter = argv[++i]; } else if (isArg(i, argc, argv, "-a", "--address", 1)) { // save listen address try { - s_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); + ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], + kDefaultPort); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, - pname, e.what(), pname)); + ARG->m_pname, e.what(), ARG->m_pname)); bye(kExitArgs); } ++i; @@ -402,11 +379,12 @@ parse(int argc, const char** argv) else if (isArg(i, argc, argv, NULL, "--http", 1)) { // save listen address try { - s_httpAddress = CNetworkAddress(argv[i + 1], kDefaultPort + 1); + ARG->m_httpAddress = CNetworkAddress(argv[i + 1], + kDefaultPort + 1); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, - pname, e.what(), pname)); + ARG->m_pname, e.what(), ARG->m_pname)); bye(kExitArgs); } ++i; @@ -414,36 +392,36 @@ parse(int argc, const char** argv) else if (isArg(i, argc, argv, "-n", "--name", 1)) { // save screen name - s_name = argv[++i]; + ARG->m_name = argv[++i]; } else if (isArg(i, argc, argv, "-c", "--config", 1)) { // save configuration file path - s_configFile = argv[++i]; + ARG->m_configFile = argv[++i]; } else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon - s_daemon = false; + ARG->m_daemon = false; } else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize - s_daemon = true; + ARG->m_daemon = true; } else if (isArg(i, argc, argv, "-1", "--no-restart")) { // don't try to restart - s_restartable = false; + ARG->m_restartable = false; } else if (isArg(i, argc, argv, NULL, "--restart")) { // try to restart - s_restartable = true; + ARG->m_restartable = true; } else if (isArg(i, argc, argv, "-z", NULL)) { - s_backend = true; + ARG->m_backend = true; } else if (isArg(i, argc, argv, "-h", "--help")) { @@ -464,7 +442,7 @@ parse(int argc, const char** argv) else if (argv[i][0] == '-') { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } @@ -477,30 +455,30 @@ parse(int argc, const char** argv) // no non-option arguments are allowed if (i != argc) { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } // increase default filter level for daemon. the user must // explicitly request another level for a daemon. - if (s_daemon && s_logFilter == NULL) { + if (ARG->m_daemon && ARG->m_logFilter == NULL) { #if WINDOWS_LIKE - if (CPlatform::isWindows95Family()) { + if (CArchMiscWindows::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. - s_logFilter = "FATAL"; + ARG->m_logFilter = "FATAL"; } else #endif { - s_logFilter = "NOTE"; + ARG->m_logFilter = "NOTE"; } } // set log filter - if (!CLog::setFilter(s_logFilter)) { + if (!CLOG->setFilter(ARG->m_logFilter)) { LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, - pname, s_logFilter, pname)); + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); bye(kExitArgs); } } @@ -518,14 +496,14 @@ loadConfig(const char* pathname, bool require) if (!configStream) { throw XConfigRead("cannot open file"); } - configStream >> s_config; + configStream >> ARG->m_config; LOG((CLOG_DEBUG "configuration read successfully")); return true; } catch (XConfigRead& e) { if (require) { LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s", - pname, pathname, e.what())); + ARG->m_pname, pathname, e.what())); bye(kExitConfig); } else { @@ -541,36 +519,46 @@ void loadConfig() { // load the config file, if specified - if (s_configFile != NULL) { + if (ARG->m_configFile != NULL) { // require the user specified file to load correctly - loadConfig(s_configFile, true); + loadConfig(ARG->m_configFile, true); } // load the default configuration if no explicit file given else { // get the user's home directory. use the effective user id // so a user can't get a setuid root program to load his file. - CPlatform platform; bool loaded = false; - CString path = platform.getUserDirectory(); + CString path = ARCH->getUserDirectory(); if (!path.empty()) { // complete path - path = platform.addPathComponent(path, USR_CONFIG_NAME); + path = ARCH->concatPath(path, USR_CONFIG_NAME); // now try loading the user's configuration loaded = loadConfig(path.c_str(), false); } if (!loaded) { // try the system-wide config file - path = platform.getSystemDirectory(); + path = ARCH->getSystemDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, SYS_CONFIG_NAME); + path = ARCH->concatPath(path, SYS_CONFIG_NAME); loadConfig(path.c_str(), false); } } } } +static +void +useSystemLog() +{ + // redirect log messages + ILogOutputter* logger = new CSystemLogOutputter; + logger->open(DAEMON_NAME); + CLOG->insert(new CStopLogOutputter); + CLOG->insert(logger); +} + // // platform dependent entry points // @@ -581,24 +569,41 @@ loadConfig() static bool s_hasImportantLogMessages = false; -static +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + bool -logMessageBox(int priority, const char* msg) +CMessageBoxOutputter::write(ELevel level, const char* message) { // note any important messages the user may need to know about - if (priority <= CLog::kWARNING) { + if (level <= CLog::kWARNING) { s_hasImportantLogMessages = true; } // FATAL and PRINT messages get a dialog box if not running as // backend. if we're running as a backend the user will have // a chance to see the messages when we exit. - if (!s_backend && priority <= CLog::kFATAL) { - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); - return true; + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; } else { - return false; + return true; } } @@ -606,7 +611,7 @@ static void byeThrow(int x) { - throw CWin32Platform::CDaemonFailed(x); + CArchMiscWindows::daemonFailed(x); } static @@ -618,10 +623,9 @@ daemonStop(void) static int -daemonStartup(IPlatform* iplatform, int argc, const char** argv) +daemonStartup(int argc, const char** argv) { - // get platform pointer - CWin32Platform* platform = static_cast(iplatform); + useSystemLog(); // catch errors that would normally exit bye = &byeThrow; @@ -630,38 +634,38 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) parse(argc, argv); // cannot run as backend if running as a service - s_backend = false; + ARG->m_backend = false; // load configuration loadConfig(); // run as a service - return platform->runDaemon(realMain, daemonStop); + return CArchMiscWindows::runDaemon(realMain, daemonStop); } static int -daemonStartup95(IPlatform*, int, const char**) +daemonStartup95(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // save instance CMSWindowsScreen::init(instance); // get program name - pname = platform.getBasename(__argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(__argv[0]); // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); + CLOG->insert(new CMessageBoxOutputter); // windows NT family starts services using no command line options. // since i'm not sure how to tell the difference between that and @@ -669,13 +673,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // arguments and we're on NT then we're being invoked as a service. // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. - if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { - int result = platform.daemonize(DAEMON_NAME, &daemonStartup); - if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); return kExitFailed; } - return result; } // parse command line @@ -686,38 +691,36 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // daemonize if requested int result; - if (s_daemon) { - // redirect log messages - platform.installDaemonLogger(DAEMON_NAME); - + if (ARG->m_daemon) { // start as a daemon - if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); - if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + if (CArchMiscWindows::isWindows95Family()) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); result = kExitFailed; } } else { // cannot start a service from the command line so just // run normally (except with log messages redirected). - result = realMain(NULL); + useSystemLog(); + result = realMain(); } } else { // run - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. - if (s_backend && s_hasImportantLogMessages) { + if (ARG->m_backend && s_hasImportantLogMessages) { char msg[1024]; msg[0] = '\0'; LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); } return result; @@ -727,21 +730,21 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) static int -daemonStartup(IPlatform*, int, const char**) +daemonStartup(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int main(int argc, char** argv) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // get program name - pname = platform.getBasename(argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(argv[0]); // parse command line parse(argc, const_cast(argv)); @@ -751,19 +754,19 @@ main(int argc, char** argv) // daemonize if requested int result; - if (s_daemon) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup); - if (result == -1) { + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { LOG((CLOG_CRIT "failed to daemonize")); - return kExitFailed; + result = kExitFailed; } } else { - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - return result; } diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp index c9853f5d..39accfa4 100644 --- a/cmd/synergys/synergys.dsp +++ b/cmd/synergys/synergys.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/configure.in b/configure.in index 6064df92..bc3e6222 100644 --- a/configure.in +++ b/configure.in @@ -13,7 +13,7 @@ dnl GNU General Public License for more details. dnl Process this file with autoconf to produce a configure script. dnl initialize -AC_INIT(lib/base/common.h) +AC_INIT(lib/common/common.h) AC_CONFIG_AUX_DIR(config) dnl current version @@ -99,7 +99,9 @@ LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" AC_OUTPUT([ Makefile lib/Makefile +lib/arch/Makefile lib/base/Makefile +lib/common/Makefile lib/mt/Makefile lib/io/Makefile lib/http/Makefile diff --git a/lib/Makefile.am b/lib/Makefile.am index 5b23e06b..cababc3e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -16,6 +16,8 @@ DEPTH = .. VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ + common \ + arch \ base \ mt \ io \ diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp new file mode 100644 index 00000000..e5e8f660 --- /dev/null +++ b/lib/arch/CArch.cpp @@ -0,0 +1,576 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" +#include "CArch.h" + +#undef ARCH_CONSOLE +#undef ARCH_DAEMON +#undef ARCH_FILE +#undef ARCH_LOG +#undef ARCH_MULTITHREAD +#undef ARCH_NETWORK +#undef ARCH_SLEEP +#undef ARCH_STRING +#undef ARCH_TIME + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchConsoleWindows.h" +# include "CArchDaemonWindows.h" +# include "CArchFileWindows.h" +# include "CArchLogWindows.h" +# include "CArchMultithreadWindows.h" +# include "CArchNetworkWinsock.h" +# include "CArchSleepWindows.h" +# include "CArchStringWindows.h" +# include "CArchTimeWindows.h" +#elif UNIX_LIKE +# include "CArchConsoleUnix.h" +# include "CArchDaemonUnix.h" +# include "CArchFileUnix.h" +# include "CArchLogUnix.h" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.h" +# endif +# include "CArchNetworkBSD.h" +# include "CArchSleepUnix.h" +# include "CArchStringUnix.h" +# include "CArchTimeUnix.h" +#endif + +#if !defined(ARCH_CONSOLE) +# error unsupported platform for console +#endif + +#if !defined(ARCH_DAEMON) +# error unsupported platform for daemon +#endif + +#if !defined(ARCH_FILE) +# error unsupported platform for file +#endif + +#if !defined(ARCH_LOG) +# error unsupported platform for logging +#endif + +#if !defined(ARCH_MULTITHREAD) +# error unsupported platform for multithreading +#endif + +#if !defined(ARCH_NETWORK) +# error unsupported platform for network +#endif + +#if !defined(ARCH_SLEEP) +# error unsupported platform for sleep +#endif + +#if !defined(ARCH_STRING) +# error unsupported platform for string +#endif + +#if !defined(ARCH_TIME) +# error unsupported platform for time +#endif + +// +// CArch +// + +CArch* CArch::s_instance = NULL; + +CArch::CArch(ARCH_ARGS) +{ + // only once instance of CArch + assert(s_instance == NULL); + s_instance = this; + + // create architecture implementation objects + m_mt = new ARCH_MULTITHREAD; + m_file = new ARCH_FILE; + m_log = new ARCH_LOG; + m_net = new ARCH_NETWORK; + m_sleep = new ARCH_SLEEP; + m_string = new ARCH_STRING; + m_time = new ARCH_TIME; + m_console = new ARCH_CONSOLE; + m_daemon = new ARCH_DAEMON; +} + +CArch::~CArch() +{ + // clean up + delete m_daemon; + delete m_console; + delete m_time; + delete m_string; + delete m_sleep; + delete m_net; + delete m_log; + delete m_file; + delete m_mt; + + // no instance + s_instance = NULL; +} + +CArch* +CArch::getInstance() +{ + return s_instance; +} + +void +CArch::openConsole(const char* title) +{ + m_console->openConsole(title); +} + +void +CArch::closeConsole() +{ + m_console->closeConsole(); +} + +void +CArch::writeConsole(const char* str) +{ + m_console->writeConsole(str); +} + +const char* +CArch::getNewlineForConsole() +{ + return m_console->getNewlineForConsole(); +} + +void +CArch::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) +{ + m_daemon->installDaemon(name, description, pathname, commandLine, allUsers); +} + +void +CArch::uninstallDaemon(const char* name, bool allUsers) +{ + m_daemon->uninstallDaemon(name, allUsers); +} + +int +CArch::daemonize(const char* name, DaemonFunc func) +{ + return m_daemon->daemonize(name, func); +} + +bool +CArch::canInstallDaemon(const char* name, bool allUsers) +{ + return m_daemon->canInstallDaemon(name, allUsers); +} + +bool +CArch::isDaemonInstalled(const char* name, bool allUsers) +{ + return m_daemon->isDaemonInstalled(name, allUsers); +} + +const char* +CArch::getBasename(const char* pathname) +{ + return m_file->getBasename(pathname); +} + +std::string +CArch::getUserDirectory() +{ + return m_file->getUserDirectory(); +} + +std::string +CArch::getSystemDirectory() +{ + return m_file->getSystemDirectory(); +} + +std::string +CArch::concatPath(const std::string& prefix, const std::string& suffix) +{ + return m_file->concatPath(prefix, suffix); +} + +void +CArch::openLog(const char* name) +{ + m_log->openLog(name); +} + +void +CArch::closeLog() +{ + m_log->closeLog(); +} + +void +CArch::writeLog(ELevel level, const char* msg) +{ + m_log->writeLog(level, msg); +} + +CArchCond +CArch::newCondVar() +{ + return m_mt->newCondVar(); +} + +void +CArch::closeCondVar(CArchCond cond) +{ + m_mt->closeCondVar(cond); +} + +void +CArch::signalCondVar(CArchCond cond) +{ + m_mt->signalCondVar(cond); +} + +void +CArch::broadcastCondVar(CArchCond cond) +{ + m_mt->broadcastCondVar(cond); +} + +bool +CArch::waitCondVar(CArchCond cond, CArchMutex mutex, double timeout) +{ + return m_mt->waitCondVar(cond, mutex, timeout); +} + +CArchMutex +CArch::newMutex() +{ + return m_mt->newMutex(); +} + +void +CArch::closeMutex(CArchMutex mutex) +{ + m_mt->closeMutex(mutex); +} + +void +CArch::lockMutex(CArchMutex mutex) +{ + m_mt->lockMutex(mutex); +} + +void +CArch::unlockMutex(CArchMutex mutex) +{ + m_mt->unlockMutex(mutex); +} + +CArchThread +CArch::newThread(ThreadFunc func, void* data) +{ + return m_mt->newThread(func, data); +} + +CArchThread +CArch::newCurrentThread() +{ + return m_mt->newCurrentThread(); +} + +CArchThread +CArch::copyThread(CArchThread thread) +{ + return m_mt->copyThread(thread); +} + +void +CArch::closeThread(CArchThread thread) +{ + m_mt->closeThread(thread); +} + +void +CArch::cancelThread(CArchThread thread) +{ + m_mt->cancelThread(thread); +} + +void +CArch::setPriorityOfThread(CArchThread thread, int n) +{ + m_mt->setPriorityOfThread(thread, n); +} + +void +CArch::testCancelThread() +{ + m_mt->testCancelThread(); +} + +bool +CArch::wait(CArchThread thread, double timeout) +{ + return m_mt->wait(thread, timeout); +} + +bool +CArch::waitForEvent(double timeout) +{ + return m_mt->waitForEvent(timeout); +} + +bool +CArch::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return m_mt->isSameThread(thread1, thread2); +} + +bool +CArch::isExitedThread(CArchThread thread) +{ + return m_mt->isExitedThread(thread); +} + +void* +CArch::getResultOfThread(CArchThread thread) +{ + return m_mt->getResultOfThread(thread); +} + +IArchMultithread::ThreadID +CArch::getIDOfThread(CArchThread thread) +{ + return m_mt->getIDOfThread(thread); +} + +CArchSocket +CArch::newSocket(EAddressFamily family, ESocketType type) +{ + return m_net->newSocket(family, type); +} + +CArchSocket +CArch::copySocket(CArchSocket s) +{ + return m_net->copySocket(s); +} + +void +CArch::closeSocket(CArchSocket s) +{ + m_net->closeSocket(s); +} + +void +CArch::closeSocketForRead(CArchSocket s) +{ + m_net->closeSocketForRead(s); +} + +void +CArch::closeSocketForWrite(CArchSocket s) +{ + m_net->closeSocketForWrite(s); +} + +void +CArch::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + m_net->bindSocket(s, addr); +} + +void +CArch::listenOnSocket(CArchSocket s) +{ + m_net->listenOnSocket(s); +} + +CArchSocket +CArch::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + return m_net->acceptSocket(s, addr); +} + +void +CArch::connectSocket(CArchSocket s, CArchNetAddress name) +{ + m_net->connectSocket(s, name); +} + +int +CArch::pollSocket(CPollEntry pe[], int num, double timeout) +{ + return m_net->pollSocket(pe, num, timeout); +} + +size_t +CArch::readSocket(CArchSocket s, void* buf, size_t len) +{ + return m_net->readSocket(s, buf, len); +} + +size_t +CArch::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + return m_net->writeSocket(s, buf, len); +} + +void +CArch::throwErrorOnSocket(CArchSocket s) +{ + m_net->throwErrorOnSocket(s); +} + +bool +CArch::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + return m_net->setBlockingOnSocket(s, blocking); +} + +bool +CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + return m_net->setNoDelayOnSocket(s, noDelay); +} + +std::string +CArch::getHostName() +{ + return m_net->getHostName(); +} + +CArchNetAddress +CArch::newAnyAddr(EAddressFamily family) +{ + return m_net->newAnyAddr(family); +} + +CArchNetAddress +CArch::copyAddr(CArchNetAddress addr) +{ + return m_net->copyAddr(addr); +} + +CArchNetAddress +CArch::nameToAddr(const std::string& name) +{ + return m_net->nameToAddr(name); +} + +void +CArch::closeAddr(CArchNetAddress addr) +{ + m_net->closeAddr(addr); +} + +std::string +CArch::addrToName(CArchNetAddress addr) +{ + return m_net->addrToName(addr); +} + +std::string +CArch::addrToString(CArchNetAddress addr) +{ + return m_net->addrToString(addr); +} + +IArchNetwork::EAddressFamily +CArch::getAddrFamily(CArchNetAddress addr) +{ + return m_net->getAddrFamily(addr); +} + +void +CArch::setAddrPort(CArchNetAddress addr, int port) +{ + m_net->setAddrPort(addr, port); +} + +int +CArch::getAddrPort(CArchNetAddress addr) +{ + return m_net->getAddrPort(addr); +} + +bool +CArch::isAnyAddr(CArchNetAddress addr) +{ + return m_net->isAnyAddr(addr); +} + +void +CArch::sleep(double timeout) +{ + m_sleep->sleep(timeout); +} + +int +CArch::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + return m_string->vsnprintf(str, size, fmt, ap); +} + +CArchMBState +CArch::newMBState() +{ + return m_string->newMBState(); +} + +void +CArch::closeMBState(CArchMBState state) +{ + m_string->closeMBState(state); +} + +void +CArch::initMBState(CArchMBState state) +{ + m_string->initMBState(state); +} + +bool +CArch::isInitMBState(CArchMBState state) +{ + return m_string->isInitMBState(state); +} + +int +CArch::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState state) +{ + return m_string->convMBToWC(dst, src, n, state); +} + +int +CArch::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + return m_string->convWCToMB(dst, src, state); +} + +double +CArch::time() +{ + return m_time->time(); +} diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h new file mode 100644 index 00000000..e12e33af --- /dev/null +++ b/lib/arch/CArch.h @@ -0,0 +1,163 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCH_H +#define CARCH_H + +#include "IArchConsole.h" +#include "IArchDaemon.h" +#include "IArchFile.h" +#include "IArchLog.h" +#include "IArchMultithread.h" +#include "IArchNetwork.h" +#include "IArchSleep.h" +#include "IArchString.h" +#include "IArchTime.h" + +#define ARCH (CArch::getInstance()) + +#define ARCH_ARGS + +class CArch : public IArchConsole, + public IArchDaemon, + public IArchFile, + public IArchLog, + public IArchMultithread, + public IArchNetwork, + public IArchSleep, + public IArchString, + public IArchTime { +public: + CArch(ARCH_ARGS); + ~CArch(); + + // + // accessors + // + + static CArch* getInstance(); + + // IArchConsole overrides + virtual void openConsole(const char*); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); + + // IArchLog overrides + virtual void openLog(const char*); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual bool waitForEvent(double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + + // IArchSleep overrides + virtual void sleep(double timeout); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + + // IArchTime overrides + virtual double time(); + +private: + static CArch* s_instance; + + IArchConsole* m_console; + IArchDaemon* m_daemon; + IArchFile* m_file; + IArchLog* m_log; + IArchMultithread* m_mt; + IArchNetwork* m_net; + IArchSleep* m_sleep; + IArchString* m_string; + IArchTime* m_time; +}; + +#endif diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp new file mode 100644 index 00000000..79c4ae2c --- /dev/null +++ b/lib/arch/CArchConsoleUnix.cpp @@ -0,0 +1,54 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleUnix.h" +#include + +// +// CArchConsoleUnix +// + +CArchConsoleUnix::CArchConsoleUnix() +{ + // do nothing +} + +CArchConsoleUnix::~CArchConsoleUnix() +{ + // do nothing +} + +void +CArchConsoleUnix::openConsole(const char*) +{ + // do nothing +} + +void +CArchConsoleUnix::closeConsole() +{ + // do nothing +} + +void +CArchConsoleUnix::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleUnix::getNewlineForConsole() +{ + return "\n"; +} diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h new file mode 100644 index 00000000..5beb8799 --- /dev/null +++ b/lib/arch/CArchConsoleUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEUNIX_H +#define CARCHCONSOLEUNIX_H + +#include "IArchConsole.h" + +#define ARCH_CONSOLE CArchConsoleUnix + +class CArchConsoleUnix : public IArchConsole { +public: + CArchConsoleUnix(); + virtual ~CArchConsoleUnix(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); +}; + +#endif diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp new file mode 100644 index 00000000..c7afb95c --- /dev/null +++ b/lib/arch/CArchConsoleWindows.cpp @@ -0,0 +1,107 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleWindows.h" +#include "CArch.h" +#include + +// +// CArchConsoleWindows +// + +DWORD CArchConsoleWindows::s_thread = 0; + +CArchConsoleWindows::CArchConsoleWindows() : + m_output(NULL) +{ + s_thread = GetCurrentThreadId(); + + m_mutex = ARCH->newMutex(); +} + +CArchConsoleWindows::~CArchConsoleWindows() +{ + ARCH->closeMutex(m_mutex); +} + +void +CArchConsoleWindows::openConsole(const char* title) +{ + ARCH->lockMutex(m_mutex); + if (m_output == NULL) { + if (AllocConsole()) { + // get console output handle + m_output = GetStdHandle(STD_ERROR_HANDLE); + + // set console title + if (title != NULL) { + SetConsoleTitle(title); + } + + // prep console. windows 95 and its ilk have braindead + // consoles that can't even resize independently of the + // buffer size. use a 25 line buffer for those systems. + OSVERSIONINFO osInfo; + COORD size = { 80, 1000 }; + osInfo.dwOSVersionInfoSize = sizeof(osInfo); + if (GetVersionEx(&osInfo) && + osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + size.Y = 25; + SetConsoleScreenBufferSize(m_output, size); + SetConsoleTextAttribute(m_output, + FOREGROUND_RED | + FOREGROUND_GREEN | + FOREGROUND_BLUE); + + // catch console signals + SetConsoleCtrlHandler(&CArchConsoleWindows::signalHandler, TRUE); + + // reopen stderr to point at console + freopen("con", "w", stderr); + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::closeConsole() +{ + ARCH->lockMutex(m_mutex); + if (m_output != NULL) { + if (FreeConsole()) { + m_output = NULL; + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleWindows::getNewlineForConsole() +{ + return "\r\n"; +} + +BOOL WINAPI +CArchConsoleWindows::signalHandler(DWORD) +{ + // terminate cleanly and skip remaining handlers + PostThreadMessage(s_thread, WM_QUIT, 0, 0); + return TRUE; +} diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h new file mode 100644 index 00000000..d93555b4 --- /dev/null +++ b/lib/arch/CArchConsoleWindows.h @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEWINDOWS_H +#define CARCHCONSOLEWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchConsole.h" +#include "IArchMultithread.h" +#include + +#define ARCH_CONSOLE CArchConsoleWindows + +class CArchConsoleWindows : public IArchConsole { +public: + CArchConsoleWindows(); + virtual ~CArchConsoleWindows(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + +private: + static BOOL WINAPI signalHandler(DWORD); + +private: + static DWORD s_thread; + + CArchMutex m_mutex; + HANDLE m_output; +}; + +#endif diff --git a/lib/arch/CArchDaemonNone.cpp b/lib/arch/CArchDaemonNone.cpp new file mode 100644 index 00000000..35a58236 --- /dev/null +++ b/lib/arch/CArchDaemonNone.cpp @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonNone.h" + +// +// CArchDaemonNone +// + +CArchDaemonNone::CArchDaemonNone() +{ + // do nothing +} + +CArchDaemonNone::~CArchDaemonNone() +{ + // do nothing +} + +void +CArchDaemonNone::installDaemon(const char*, + const char*, + const char*, + const char*, + bool) +{ + // do nothing +} + +void +CArchDaemonNone::uninstallDaemon(const char*, bool) +{ + // do nothing +} + +int +CArchDaemonNone::daemonize(const char* name, DaemonFunc func) +{ + // simply forward the call to func. obviously, this doesn't + // do any daemonizing. + return func(1, &name); +} + +bool +CArchDaemonNone::canInstallDaemon(const char*, bool) +{ + return false; +} + +bool +CArchDaemonNone::isDaemonInstalled(const char* name, bool allUsers) +{ + return false; +} diff --git a/lib/arch/CArchDaemonNone.h b/lib/arch/CArchDaemonNone.h new file mode 100644 index 00000000..8e017bdb --- /dev/null +++ b/lib/arch/CArchDaemonNone.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONNONE_H +#define CARCHDAEMONNONE_H + +#include "IArchDaemon.h" + +#define ARCH_DAEMON CArchDaemonNone + +class CArchDaemonNone : public IArchDaemon { +public: + CArchDaemonNone(); + virtual ~CArchDaemonNone(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); +}; + +#endif diff --git a/lib/arch/CArchDaemonUnix.cpp b/lib/arch/CArchDaemonUnix.cpp new file mode 100644 index 00000000..748780db --- /dev/null +++ b/lib/arch/CArchDaemonUnix.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonUnix.h" +#include "XArchUnix.h" +#include +#include +#include +#include +#include + +// we derive from CArchDaemonNone +#include "CArchDaemonNone.cpp" + +// +// CArchDaemonUnix +// + +CArchDaemonUnix::CArchDaemonUnix() +{ + // do nothing +} + +CArchDaemonUnix::~CArchDaemonUnix() +{ + // do nothing +} + +int +CArchDaemonUnix::daemonize(const char* name, DaemonFunc func) +{ + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + throw XArchDaemonFailed(new XArchEvalUnix(errno)); + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + + // chdir to root so we don't keep mounted filesystems points busy + chdir("/"); + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + dup(1); + + // invoke function + return func(1, &name); +} diff --git a/lib/arch/CArchDaemonUnix.h b/lib/arch/CArchDaemonUnix.h new file mode 100644 index 00000000..b4c82daa --- /dev/null +++ b/lib/arch/CArchDaemonUnix.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONUNIX_H +#define CARCHDAEMONUNIX_H + +#include "CArchDaemonNone.h" + +#undef ARCH_DAEMON +#define ARCH_DAEMON CArchDaemonUnix + +class CArchDaemonUnix : public CArchDaemonNone { +public: + CArchDaemonUnix(); + virtual ~CArchDaemonUnix(); + + // IArchDaemon overrides + virtual int daemonize(const char* name, DaemonFunc func); +}; + +#endif diff --git a/lib/platform/CWin32Platform.cpp b/lib/arch/CArchDaemonWindows.cpp similarity index 53% rename from lib/platform/CWin32Platform.cpp rename to lib/arch/CArchDaemonWindows.cpp index fc1fa4ae..c63d48ea 100644 --- a/lib/platform/CWin32Platform.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -12,103 +12,80 @@ * GNU General Public License for more details. */ -#include "CWin32Platform.h" -#include "CLock.h" -#include "CThread.h" -#include "CLog.h" -#include "TMethodJob.h" +#include "CArchDaemonWindows.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "XArchWindows.h" #include "stdvector.h" -#include -#include -#include // -// CWin32Platform +// CArchDaemonWindows // -HANDLE CWin32Platform::s_eventLog = NULL; -CWin32Platform* CWin32Platform::s_daemonPlatform = NULL; +CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; -CWin32Platform::CWin32Platform() +CArchDaemonWindows::CArchDaemonWindows() { // do nothing } -CWin32Platform::~CWin32Platform() +CArchDaemonWindows::~CArchDaemonWindows() { // do nothing } -bool -CWin32Platform::isWindows95Family() +int +CArchDaemonWindows::runDaemon(RunFunc runFunc, StopFunc stopFunc) { - OSVERSIONINFO version; - version.dwOSVersionInfoSize = sizeof(version); - if (GetVersionEx(&version) == 0) { - LOG((CLOG_WARN "cannot determine OS: %d", GetLastError())); - return true; + assert(s_daemon != NULL); + + return s_daemon->doRunDaemon(runFunc, stopFunc); +} + +void +CArchDaemonWindows::daemonRunning(bool running) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + s_daemon->doDaemonRunning(running); } - return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } void -CWin32Platform::setStatus(SERVICE_STATUS_HANDLE handle, DWORD state) +CArchDaemonWindows::daemonFailed(int result) { - setStatus(handle, state, 0, 0); + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + throw XArchDaemonRunFailed(result); + } } void -CWin32Platform::setStatus(SERVICE_STATUS_HANDLE handle, - DWORD state, DWORD step, DWORD waitHint) -{ - SERVICE_STATUS status; - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | - SERVICE_INTERACTIVE_PROCESS; - status.dwCurrentState = state; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP | - SERVICE_ACCEPT_PAUSE_CONTINUE | - SERVICE_ACCEPT_SHUTDOWN; - status.dwWin32ExitCode = NO_ERROR; - status.dwServiceSpecificExitCode = 0; - status.dwCheckPoint = step; - status.dwWaitHint = waitHint; - SetServiceStatus(handle, &status); -} - -void -CWin32Platform::setStatusError(SERVICE_STATUS_HANDLE handle, DWORD error) -{ - SERVICE_STATUS status; - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | - SERVICE_INTERACTIVE_PROCESS; - status.dwCurrentState = SERVICE_STOPPED; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP | - SERVICE_ACCEPT_PAUSE_CONTINUE | - SERVICE_ACCEPT_SHUTDOWN; - status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - status.dwServiceSpecificExitCode = error; - status.dwCheckPoint = 0; - status.dwWaitHint = 0; - SetServiceStatus(handle, &status); -} - -bool -CWin32Platform::installDaemon(const char* name, const char* description, - const char* pathname, const char* commandLine, bool allUsers) +CArchDaemonWindows::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // open registry - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); if (key == NULL) { - LOG((CLOG_ERR "cannot open registry key", GetLastError())); - return false; + // can't open key + throw XArchDaemonInstallFailed(new XArchEvalWindows); } // construct entry - CString value; + std::string value; value += "\""; value += pathname; value += "\" "; @@ -119,8 +96,6 @@ CWin32Platform::installDaemon(const char* name, const char* description, // clean up closeKey(key); - - return true; } // windows NT family services @@ -128,11 +103,11 @@ CWin32Platform::installDaemon(const char* name, const char* description, // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { - LOG((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); - return false; + // can't open service manager + throw XArchDaemonInstallFailed(new XArchEvalWindows); } - // create the servie + // create the service SC_HANDLE service = CreateService(mgr, name, name, @@ -147,60 +122,69 @@ CWin32Platform::installDaemon(const char* name, const char* description, NULL, NULL, NULL); + if (service == NULL) { + // can't create service + // FIXME -- handle ERROR_SERVICE_EXISTS + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } // done with service and manager - if (service != NULL) { - CloseServiceHandle(service); - CloseServiceHandle(mgr); - } - else { -// FIXME -- handle ERROR_SERVICE_EXISTS - - LOG((CLOG_ERR "CreateService failed with %d", GetLastError())); - CloseServiceHandle(mgr); - return false; - } + CloseServiceHandle(service); + CloseServiceHandle(mgr); // open the registry key for this service HKEY key = openNTServicesKey(); key = openKey(key, name); if (key == NULL) { // can't open key - uninstallDaemon(name, allUsers); - return false; + DWORD err = GetLastError(); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); } // set the description - setValue(key, "Description", description); + setValue(key, _T("Description"), description); // set command line - key = openKey(key, "Parameters"); + key = openKey(key, _T("Parameters")); if (key == NULL) { // can't open key - uninstallDaemon(name, allUsers); - return false; + DWORD err = GetLastError(); + closeKey(key); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); } - setValue(key, "CommandLine", commandLine); + setValue(key, _T("CommandLine"), commandLine); // done with registry closeKey(key); - - return true; } } -IPlatform::EResult -CWin32Platform::uninstallDaemon(const char* name, bool allUsers) +void +CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // open registry - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); if (key == NULL) { - LOG((CLOG_ERR "cannot open registry key", GetLastError())); - return kAlready; + // can't open key. daemon is probably not installed. + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows); } // remove entry @@ -208,97 +192,90 @@ CWin32Platform::uninstallDaemon(const char* name, bool allUsers) // clean up closeKey(key); - - return kSuccess; } // windows NT family services else { - // remove parameters for this service + // remove parameters for this service. ignore failures. HKEY key = openNTServicesKey(); key = openKey(key, name); if (key != NULL) { - deleteKey(key, "Parameters"); + deleteKey(key, _T("Parameters")); closeKey(key); } // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { - LOG((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); - return kFailed; + // can't open service manager + throw XArchDaemonUninstallFailed(new XArchEvalWindows); } // open the service. oddly, you must open a service to delete it. - EResult result; SC_HANDLE service = OpenService(mgr, name, DELETE); if (service == NULL) { - const DWORD e = GetLastError(); - LOG((CLOG_ERR "OpenService failed with %d", e)); - result = (e == ERROR_SERVICE_DOES_NOT_EXIST) ? kAlready : kFailed; + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + if (err != ERROR_SERVICE_DOES_NOT_EXIST) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); } - else { - if (DeleteService(service) != 0) { - result = kSuccess; - } - else { - const DWORD e = GetLastError(); - switch (e) { - case ERROR_SERVICE_MARKED_FOR_DELETE: - result = kAlready; - break; + // delete the service + const bool okay = (DeleteService(service) == 0); + const DWORD err = GetLastError(); - default: - result = kFailed; - break; - } - } - CloseServiceHandle(service); - } - - // close the manager + // clean up + CloseServiceHandle(service); CloseServiceHandle(mgr); - return result; + // handle failure. ignore error if service isn't installed anymore. + if (!okay && isDaemonInstalled(name, allUsers)) { + if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } } } int -CWin32Platform::daemonize(const char* name, DaemonFunc func) +CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) { assert(name != NULL); assert(func != NULL); // windows 95 family services - if (isWindows95Family()) { + if (CArchMiscWindows::isWindows95Family()) { typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD); // mark this process as a service so it's not killed when the // user logs off. HINSTANCE kernel = LoadLibrary("kernel32.dll"); if (kernel == NULL) { - LOG((CLOG_ERR "LoadLibrary failed with %d", GetLastError())); - return -1; + throw XArchDaemonFailed(new XArchEvalWindows); } RegisterServiceProcessT RegisterServiceProcess = reinterpret_cast( GetProcAddress(kernel, - _T("RegisterServiceProcess"))); + "RegisterServiceProcess")); if (RegisterServiceProcess == NULL) { - LOG((CLOG_ERR "can't lookup RegisterServiceProcess: %d", GetLastError())); + // missing RegisterServiceProcess function + DWORD err = GetLastError(); FreeLibrary(kernel); - return -1; + throw XArchDaemonFailed(new XArchEvalWindows(err)); } if (RegisterServiceProcess(NULL, 1) == 0) { - LOG((CLOG_ERR "RegisterServiceProcess failed with %d", GetLastError())); + // RegisterServiceProcess failed + DWORD err = GetLastError(); FreeLibrary(kernel); - return -1; + throw XArchDaemonFailed(new XArchEvalWindows(err)); } FreeLibrary(kernel); // now simply call the daemon function - return func(this, 1, &name); + return func(1, &name); } // windows NT family services @@ -309,46 +286,33 @@ CWin32Platform::daemonize(const char* name, DaemonFunc func) // construct the service entry SERVICE_TABLE_ENTRY entry[2]; entry[0].lpServiceName = const_cast(name); - entry[0].lpServiceProc = &CWin32Platform::serviceMainEntry; + entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry; entry[1].lpServiceName = NULL; entry[1].lpServiceProc = NULL; // hook us up to the service control manager. this won't return // (if successful) until the processes have terminated. - s_daemonPlatform = this; + s_daemon = this; if (StartServiceCtrlDispatcher(entry)) { - s_daemonPlatform = NULL; - return m_daemonResult; + // StartServiceCtrlDispatcher failed + s_daemon = NULL; + throw XArchDaemonFailed(new XArchEvalWindows); } - LOG((CLOG_ERR "StartServiceCtrlDispatcher failed with %d", GetLastError())); - s_daemonPlatform = NULL; - return -1; - } -} -void -CWin32Platform::installDaemonLogger(const char* name) -{ - if (!CWin32Platform::isWindows95Family()) { - // open event log and direct log messages to it - if (s_eventLog == NULL) { - s_eventLog = RegisterEventSource(NULL, name); - if (s_eventLog != NULL) { - CLog::setOutputter(&CWin32Platform::serviceLogger); - } - } + s_daemon = NULL; + return m_daemonResult; } } bool -CWin32Platform::canInstallDaemon(const char* name, bool allUsers) const +CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // check if we can open the registry key - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); closeKey(key); return (key != NULL); } @@ -365,7 +329,7 @@ CWin32Platform::canInstallDaemon(const char* name, bool allUsers) const // check if we can open the registry key for this service HKEY key = openNTServicesKey(); key = openKey(key, name); - key = openKey(key, "Parameters"); + key = openKey(key, _T("Parameters")); closeKey(key); return (key != NULL); @@ -373,14 +337,14 @@ CWin32Platform::canInstallDaemon(const char* name, bool allUsers) const } bool -CWin32Platform::isDaemonInstalled(const char* name, bool allUsers) const +CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // check if we can open the registry key - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); if (key == NULL) { return false; } @@ -399,9 +363,10 @@ CWin32Platform::isDaemonInstalled(const char* name, bool allUsers) const // check parameters for this service HKEY key = openNTServicesKey(); key = openKey(key, name); - key = openKey(key, "Parameters"); + key = openKey(key, _T("Parameters")); if (key != NULL) { - const bool installed = !readValueString(key, "CommandLine").empty(); + const bool installed = !readValueString(key, + _T("CommandLine")).empty(); closeKey(key); if (!installed) { return false; @@ -418,114 +383,17 @@ CWin32Platform::isDaemonInstalled(const char* name, bool allUsers) const SC_HANDLE service = OpenService(mgr, name, GENERIC_READ); // clean up - CloseServiceHandle(service); + if (service != NULL) { + CloseServiceHandle(service); + } CloseServiceHandle(mgr); return (service != NULL); } } -const char* -CWin32Platform::getBasename(const char* pathname) const -{ - if (pathname == NULL) { - return NULL; - } - - // check for last / - const char* basename = strrchr(pathname, '/'); - if (basename != NULL) { - ++basename; - } - else { - basename = pathname; - } - - // check for last backslash - const char* basename2 = strrchr(pathname, '\\'); - if (basename2 != NULL && basename2 > basename) { - basename = basename2 + 1; - } - - return basename; -} - -CString -CWin32Platform::getUserDirectory() const -{ - // try %HOMEPATH% - TCHAR dir[MAX_PATH]; - DWORD size = sizeof(dir) / sizeof(TCHAR); - DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size); - if (result != 0 && result <= size) { - // sanity check -- if dir doesn't appear to start with a - // drive letter and isn't a UNC name then don't use it - // FIXME -- allow UNC names - if (dir[0] != '\0' && (dir[1] == ':' || - ((dir[0] == '\\' || dir[0] == '/') && - (dir[1] == '\\' || dir[1] == '/')))) { - return dir; - } - } - - // get the location of the personal files. that's as close to - // a home directory as we're likely to find. - ITEMIDLIST* idl; - if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) { - TCHAR* path = NULL; - if (SHGetPathFromIDList(idl, dir)) { - DWORD attr = GetFileAttributes(dir); - if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) - path = dir; - } - - IMalloc* shalloc; - if (SUCCEEDED(SHGetMalloc(&shalloc))) { - shalloc->Free(idl); - shalloc->Release(); - } - - if (path != NULL) { - return path; - } - } - - // use root of C drive as a default - return "C:"; -} - -CString -CWin32Platform::getSystemDirectory() const -{ - // get windows directory - char dir[MAX_PATH]; - if (GetWindowsDirectory(dir, sizeof(dir)) != 0) { - return dir; - } - else { - // can't get it. use C:\ as a default. - return "C:"; - } -} - -CString -CWin32Platform::addPathComponent(const CString& prefix, - const CString& suffix) const -{ - CString path; - path.reserve(prefix.size() + 1 + suffix.size()); - path += prefix; - if (path.size() == 0 || - (path[path.size() - 1] != '\\' && - path[path.size() - 1] != '/')) { - path += '\\'; - } - path += suffix; - return path; -} - HKEY -CWin32Platform::openKey(HKEY key, const char* keyName) +CArchDaemonWindows::openKey(HKEY key, const TCHAR* keyName) { // ignore if parent is NULL if (key == NULL) { @@ -553,9 +421,9 @@ CWin32Platform::openKey(HKEY key, const char* keyName) } HKEY -CWin32Platform::openKey(HKEY key, const char** keyNames) +CArchDaemonWindows::openKey(HKEY key, const TCHAR** keyNames) { - for (UInt32 i = 0; key != NULL && keyNames[i] != NULL; ++i) { + for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { // open next key key = openKey(key, keyNames[i]); } @@ -563,14 +431,14 @@ CWin32Platform::openKey(HKEY key, const char** keyNames) } void -CWin32Platform::closeKey(HKEY key) +CArchDaemonWindows::closeKey(HKEY key) { assert(key != NULL); RegCloseKey(key); } void -CWin32Platform::deleteKey(HKEY key, const char* name) +CArchDaemonWindows::deleteKey(HKEY key, const TCHAR* name) { assert(key != NULL); assert(name != NULL); @@ -578,7 +446,7 @@ CWin32Platform::deleteKey(HKEY key, const char* name) } void -CWin32Platform::deleteValue(HKEY key, const char* name) +CArchDaemonWindows::deleteValue(HKEY key, const TCHAR* name) { assert(key != NULL); assert(name != NULL); @@ -586,7 +454,8 @@ CWin32Platform::deleteValue(HKEY key, const char* name) } void -CWin32Platform::setValue(HKEY key, const char* name, const CString& value) +CArchDaemonWindows::setValue(HKEY key, + const TCHAR* name, const std::string& value) { assert(key != NULL); assert(name != NULL); @@ -595,15 +464,15 @@ CWin32Platform::setValue(HKEY key, const char* name, const CString& value) value.size() + 1); } -CString -CWin32Platform::readValueString(HKEY key, const char* name) +std::string +CArchDaemonWindows::readValueString(HKEY key, const TCHAR* name) { // get the size of the string DWORD type; DWORD size = 0; LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); if (result != ERROR_SUCCESS || type != REG_SZ) { - return CString(); + return std::string(); } // allocate space @@ -614,17 +483,17 @@ CWin32Platform::readValueString(HKEY key, const char* name) reinterpret_cast(buffer), &size); if (result != ERROR_SUCCESS || type != REG_SZ) { delete[] buffer; - return CString(); + return std::string(); } // clean up and return value - CString value(buffer); + std::string value(buffer); delete[] buffer; return value; } HKEY -CWin32Platform::openNTServicesKey() +CArchDaemonWindows::openNTServicesKey() { static const char* s_keyNames[] = { _T("SYSTEM"), @@ -637,7 +506,7 @@ CWin32Platform::openNTServicesKey() } HKEY -CWin32Platform::open95ServicesKey() +CArchDaemonWindows::open95ServicesKey() { static const char* s_keyNames[] = { _T("Software"), @@ -652,7 +521,7 @@ CWin32Platform::open95ServicesKey() } HKEY -CWin32Platform::openUserStartupKey() +CArchDaemonWindows::openUserStartupKey() { static const char* s_keyNames[] = { _T("Software"), @@ -667,12 +536,14 @@ CWin32Platform::openUserStartupKey() } int -CWin32Platform::runDaemon(RunFunc run, StopFunc stop) +CArchDaemonWindows::doRunDaemon(RunFunc run, StopFunc stop) { // should only be called from DaemonFunc assert(m_serviceMutex != NULL); + assert(run != NULL); + assert(stop != NULL); - CLock lock(m_serviceMutex); + ARCH->lockMutex(m_serviceMutex); try { int result; m_stop = stop; @@ -680,14 +551,16 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) m_serviceRunning = false; for (;;) { // mark server as running - setStatus(m_statusHandle, SERVICE_RUNNING); + setStatus(SERVICE_RUNNING); // run callback in another thread m_serviceRunning = true; { - CThread thread(new TMethodJob(this, - &CWin32Platform::runDaemonThread, run)); - result = reinterpret_cast(thread.getResult()); + CArchThread thread = ARCH->newThread( + &CArchDaemonWindows::runDaemonThreadEntry, run); + ARCH->wait(thread, -1.0); + result = reinterpret_cast(ARCH->getResultOfThread(thread)); + ARCH->closeThread(thread); } m_serviceRunning = false; @@ -696,36 +569,37 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) // quit. if (m_serviceHandlerWaiting) { m_serviceHandlerWaiting = false; - m_serviceState->broadcast(); + ARCH->broadcastCondVar(m_serviceCondVar); } else { break; } // wait until we're told what to do next - while (*m_serviceState != SERVICE_RUNNING && - *m_serviceState != SERVICE_STOPPED) { - m_serviceState->wait(); + while (m_serviceState != SERVICE_RUNNING && + m_serviceState != SERVICE_STOPPED) { + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } // exit loop if we've been told to stop - if (*m_serviceState == SERVICE_STOPPED) { + if (m_serviceState == SERVICE_STOPPED) { break; } } // prevent daemonHandler from changing state - *m_serviceState = SERVICE_STOPPED; + m_serviceState = SERVICE_STOPPED; // tell service control that the service is stopped. // FIXME -- hopefully this will ensure that our handler won't // be called again but i can't find documentation that // verifies that. if it does it'll crash on the mutex that // we're about to destroy. - setStatus(m_statusHandle, *m_serviceState); + setStatus(m_serviceState); // clean up m_stop = NULL; + ARCH->unlockMutex(m_serviceMutex); return result; } @@ -733,58 +607,116 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) // FIXME -- report error // prevent serviceHandler from changing state - *m_serviceState = SERVICE_STOPPED; + m_serviceState = SERVICE_STOPPED; // set status - setStatusError(m_statusHandle, 0); + setStatusError(0); // wake up serviceHandler if it's waiting then wait for it if (m_serviceHandlerWaiting) { m_serviceHandlerWaiting = false; - m_serviceState->broadcast(); - m_serviceState->wait(); + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); // serviceHandler has exited by now } + ARCH->unlockMutex(m_serviceMutex); throw; } } void -CWin32Platform::runDaemonThread(void* vrun) +CArchDaemonWindows::doDaemonRunning(bool running) { - RunFunc run = reinterpret_cast(vrun); - CThread::exit(reinterpret_cast(run(m_serviceMutex))); + if (running) { + ARCH->unlockMutex(m_serviceMutex); + } + else { + ARCH->lockMutex(m_serviceMutex); + } +} + +void* +CArchDaemonWindows::runDaemonThread(RunFunc run) +{ + return reinterpret_cast(run()); +} + +void* +CArchDaemonWindows::runDaemonThreadEntry(void* vrun) +{ + assert(s_daemon != NULL); + + return s_daemon->runDaemonThread(reinterpret_cast(vrun)); } void -CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) +CArchDaemonWindows::setStatus(DWORD state) +{ + setStatus(state, 0, 0); +} + +void +CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) +{ + assert(s_daemon != NULL); + + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = step; + status.dwWaitHint = waitHint; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::setStatusError(DWORD error) +{ + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwServiceSpecificExitCode = error; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) { typedef std::vector ArgList; - typedef std::vector Arguments; + typedef std::vector Arguments; const char** argv = const_cast(argvIn); - // open event log and direct log messages to it - installDaemonLogger(argv[0]); - // create synchronization objects - CThread::init(); - m_serviceMutex = new CMutex; - m_serviceState = new CCondVar(m_serviceMutex, SERVICE_RUNNING); + m_serviceMutex = ARCH->newMutex(); + m_serviceCondVar = ARCH->newCondVar(); + m_serviceState = SERVICE_RUNNING; // register our service handler functiom m_statusHandle = RegisterServiceCtrlHandler(argv[0], - &CWin32Platform::serviceHandlerEntry); + &CArchDaemonWindows::serviceHandlerEntry); if (m_statusHandle == NULL) { // cannot start as service m_daemonResult = -1; - delete m_serviceState; - delete m_serviceMutex; + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); return; } // tell service control manager that we're starting - setStatus(m_statusHandle, SERVICE_START_PENDING, 0, 1000); + setStatus(SERVICE_START_PENDING, 0, 1000); // if no arguments supplied then try getting them from the registry. // the first argument doesn't count because it's the service name. @@ -792,28 +724,28 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) ArgList myArgv; if (argc <= 1) { // read command line - CString commandLine; + std::string commandLine; HKEY key = openNTServicesKey(); - key = openKey(key, argv[0]); - key = openKey(key, "Parameters"); + key = openKey(key, argvIn[0]); + key = openKey(key, _T("Parameters")); if (key != NULL) { - commandLine = readValueString(key, "CommandLine"); + commandLine = readValueString(key, _T("CommandLine")); } // if the command line isn't empty then parse and use it if (!commandLine.empty()) { // parse, honoring double quoted substrings - CString::size_type i = commandLine.find_first_not_of(" \t"); - while (i != CString::npos && i != commandLine.size()) { + std::string::size_type i = commandLine.find_first_not_of(" \t"); + while (i != std::string::npos && i != commandLine.size()) { // find end of string - CString::size_type e; + std::string::size_type e; if (commandLine[i] == '\"') { // quoted. find closing quote. ++i; e = commandLine.find("\"", i); // whitespace must follow closing quote - if (e == CString::npos || + if (e == std::string::npos || (e + 1 != commandLine.size() && commandLine[e + 1] != ' ' && commandLine[e + 1] != '\t')) { @@ -828,7 +760,7 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) else { // unquoted. find next whitespace. e = commandLine.find_first_of(" \t", i); - if (e == CString::npos) { + if (e == std::string::npos) { e = commandLine.size(); } @@ -845,7 +777,7 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) myArgv.push_back(argv[0]); // get pointers - for (UInt32 i = 0; i < args.size(); ++i) { + for (size_t i = 0; i < args.size(); ++i) { myArgv.push_back(args[i].c_str()); } @@ -857,93 +789,93 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) try { // invoke daemon function - m_daemonResult = m_daemonFunc(this, static_cast(argc), argv); + m_daemonResult = m_daemonFunc(static_cast(argc), argv); } - catch (CDaemonFailed& e) { - setStatusError(m_statusHandle, e.m_result); + catch (XArchDaemonRunFailed& e) { + setStatusError(e.m_result); m_daemonResult = -1; } catch (...) { - setStatusError(m_statusHandle, 1); + setStatusError(1); m_daemonResult = -1; } // clean up - delete m_serviceState; - delete m_serviceMutex; - - // FIXME -- close event log? + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); } void WINAPI -CWin32Platform::serviceMainEntry(DWORD argc, LPTSTR* argv) +CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv) { - s_daemonPlatform->serviceMain(argc, argv); + s_daemon->serviceMain(argc, argv); } void -CWin32Platform::serviceHandler(DWORD ctrl) +CArchDaemonWindows::serviceHandler(DWORD ctrl) { - assert(m_serviceMutex != NULL); - assert(m_serviceState != NULL); + assert(m_serviceMutex != NULL); + assert(m_serviceCondVar != NULL); - CLock lock(m_serviceMutex); + ARCH->lockMutex(m_serviceMutex); // ignore request if service is already stopped - if (*m_serviceState == SERVICE_STOPPED) { - setStatus(m_statusHandle, *m_serviceState); + if (m_serviceState == SERVICE_STOPPED) { + setStatus(m_serviceState); + ARCH->unlockMutex(m_serviceMutex); return; } switch (ctrl) { case SERVICE_CONTROL_PAUSE: // update state - *m_serviceState = SERVICE_PAUSE_PENDING; - setStatus(m_statusHandle, *m_serviceState, 0, 1000); + m_serviceState = SERVICE_PAUSE_PENDING; + setStatus(m_serviceState, 0, 1000); // stop run callback if running and wait for it to finish if (m_serviceRunning) { m_serviceHandlerWaiting = true; m_stop(); - m_serviceState->wait(); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } // update state if service hasn't stopped while we were waiting - if (*m_serviceState != SERVICE_STOPPED) { - *m_serviceState = SERVICE_PAUSED; + if (m_serviceState != SERVICE_STOPPED) { + m_serviceState = SERVICE_PAUSED; } - m_serviceState->broadcast(); + ARCH->broadcastCondVar(m_serviceCondVar); break; case SERVICE_CONTROL_CONTINUE: // required status update - setStatus(m_statusHandle, *m_serviceState); + setStatus(m_serviceState); // update state but let main loop send RUNNING notification - *m_serviceState = SERVICE_RUNNING; - m_serviceState->broadcast(); + m_serviceState = SERVICE_RUNNING; + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->unlockMutex(m_serviceMutex); return; case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: // update state - *m_serviceState = SERVICE_STOP_PENDING; - setStatus(m_statusHandle, *m_serviceState, 0, 1000); + m_serviceState = SERVICE_STOP_PENDING; + setStatus(m_serviceState, 0, 1000); // stop run callback if running and wait for it to finish if (m_serviceRunning) { m_serviceHandlerWaiting = true; m_stop(); - m_serviceState->wait(); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } // update state - *m_serviceState = SERVICE_STOPPED; - m_serviceState->broadcast(); + m_serviceState = SERVICE_STOPPED; + ARCH->broadcastCondVar(m_serviceCondVar); break; default: - LOG((CLOG_WARN "unknown service command: %d", ctrl)); + // unknown service command // fall through case SERVICE_CONTROL_INTERROGATE: @@ -951,51 +883,13 @@ CWin32Platform::serviceHandler(DWORD ctrl) } // send update - setStatus(m_statusHandle, *m_serviceState); + setStatus(m_serviceState); + + ARCH->unlockMutex(m_serviceMutex); } void WINAPI -CWin32Platform::serviceHandlerEntry(DWORD ctrl) +CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl) { - s_daemonPlatform->serviceHandler(ctrl); -} - -bool -CWin32Platform::serviceLogger(int priority, const char* msg) -{ - if (s_eventLog == NULL) { - return false; - } - - // convert priority - WORD type; - switch (priority) { - case CLog::kFATAL: - case CLog::kERROR: - type = EVENTLOG_ERROR_TYPE; - break; - - case CLog::kWARNING: - type = EVENTLOG_WARNING_TYPE; - break; - - default: - type = EVENTLOG_INFORMATION_TYPE; - break; - } - - // log it - // FIXME -- win32 wants to use a message table to look up event - // strings. log messages aren't organized that way so we'll - // just dump our string into the raw data section of the event - // so users can at least see the message. note that we use our - // priority as the event category. - ReportEvent(s_eventLog, type, static_cast(priority), - 0, // event ID - NULL, - 0, - strlen(msg + 1), // raw data size - NULL, - const_cast(msg));// raw data - return true; + s_daemon->serviceHandler(ctrl); } diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h new file mode 100644 index 00000000..aeb3916f --- /dev/null +++ b/lib/arch/CArchDaemonWindows.h @@ -0,0 +1,130 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONWINDOWS_H +#define CARCHDAEMONWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchDaemon.h" +#include "IArchMultithread.h" +#include "stdstring.h" +#include +#include + +#define ARCH_DAEMON CArchDaemonWindows + +class CArchDaemonWindows : public IArchDaemon { +public: + typedef int (*RunFunc)(void); + typedef void (*StopFunc)(void); + + CArchDaemonWindows(); + virtual ~CArchDaemonWindows(); + + //! Run the daemon + /*! + When the client calls \c daemonize(), the \c DaemonFunc should call this + function after initialization and argument parsing to perform the + daemon processing. The \c runFunc should perform the daemon's + main loop, calling \c daemonRunning(true) when it enters the main loop + (i.e. after initialization) and \c daemonRunning(false) when it leaves + the main loop. The \c stopFunc function is called when the daemon + must exit the main loop and must cause \c runFunc to return. This + function returns what \c runFunc returns. \c runFunc should call + \c daemonFailed() if the daemon fails. + */ + static int runDaemon(RunFunc runFunc, StopFunc stopFunc); + + //! Indicate daemon is in main loop + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate when it has entered (\c running is \c true) or exited + (\c running is \c false) the main loop. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate failure. \c result is returned by \c daemonize(). + */ + static void daemonFailed(int result); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + +private: + static HKEY openKey(HKEY parent, const TCHAR*); + static HKEY openKey(HKEY parent, const TCHAR**); + static void closeKey(HKEY); + static void deleteKey(HKEY, const TCHAR* name); + static void deleteValue(HKEY, const TCHAR* name); + static void setValue(HKEY, const TCHAR* name, + const std::string& value); + static std::string readValueString(HKEY, const TCHAR* name); + static HKEY openNTServicesKey(); + static HKEY open95ServicesKey(); + static HKEY openUserStartupKey(); + + int doRunDaemon(RunFunc runFunc, StopFunc stopFunc); + void doDaemonRunning(bool running); + + static void setStatus(DWORD state); + static void setStatus(DWORD state, DWORD step, DWORD waitHint); + static void setStatusError(DWORD error); + + void* runDaemonThread(RunFunc); + static void* runDaemonThreadEntry(void*); + + void serviceMain(DWORD, LPTSTR*); + static void WINAPI serviceMainEntry(DWORD, LPTSTR*); + + void serviceHandler(DWORD ctrl); + static void WINAPI serviceHandlerEntry(DWORD ctrl); + +private: + class XArchDaemonRunFailed { + public: + XArchDaemonRunFailed(int result) : m_result(result) { } + + public: + int m_result; + }; + +private: + static CArchDaemonWindows* s_daemon; + + CArchMutex m_serviceMutex; + CArchCond m_serviceCondVar; + DWORD m_serviceState; + bool m_serviceHandlerWaiting; + bool m_serviceRunning; + StopFunc m_stop; + + DaemonFunc m_daemonFunc; + int m_daemonResult; + + SERVICE_STATUS_HANDLE m_statusHandle; +}; + +#endif diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp new file mode 100644 index 00000000..a708cc8d --- /dev/null +++ b/lib/arch/CArchFileUnix.cpp @@ -0,0 +1,94 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileUnix.h" +#include +#include +#include +#include + +// +// CArchFileUnix +// + +CArchFileUnix::CArchFileUnix() +{ + // do nothing +} + +CArchFileUnix::~CArchFileUnix() +{ + // do nothing +} + +const char* +CArchFileUnix::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + return basename + 1; + } + else { + return pathname; + } +} + +std::string +CArchFileUnix::getUserDirectory() +{ +#if HAVE_GETPWUID_R + struct passwd pwent; + struct passwd* pwentp; +#if defined(_SC_GETPW_R_SIZE_MAX) + long size = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + long size = BUFSIZ; +#endif + char* buffer = new char[size]; + getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); + delete[] buffer; +#else + struct passwd* pwentp = getpwuid(getuid()); +#endif + if (pwentp != NULL && pwentp->pw_dir != NULL) { + return pwentp->pw_dir; + } + else { + return std::string(); + } +} + +std::string +CArchFileUnix::getSystemDirectory() +{ + return "/etc"; +} + +std::string +CArchFileUnix::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || path[path.size() - 1] != '/') { + path += '/'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileUnix.h b/lib/arch/CArchFileUnix.h new file mode 100644 index 00000000..c6257555 --- /dev/null +++ b/lib/arch/CArchFileUnix.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEUNIX_H +#define CARCHFILEUNIX_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileUnix + +class CArchFileUnix : public IArchFile { +public: + CArchFileUnix(); + virtual ~CArchFileUnix(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchFileWindows.cpp b/lib/arch/CArchFileWindows.cpp new file mode 100644 index 00000000..7d9fd51b --- /dev/null +++ b/lib/arch/CArchFileWindows.cpp @@ -0,0 +1,132 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileWindows.h" +#include +#include +#include +#include + +// +// CArchFileWindows +// + +CArchFileWindows::CArchFileWindows() +{ + // do nothing +} + +CArchFileWindows::~CArchFileWindows() +{ + // do nothing +} + +const char* +CArchFileWindows::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + // check for last slash + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + ++basename; + } + else { + basename = pathname; + } + + // check for last backslash + const char* basename2 = strrchr(pathname, '\\'); + if (basename2 != NULL && basename2 > basename) { + basename = basename2 + 1; + } + + return basename; +} + +std::string +CArchFileWindows::getUserDirectory() +{ + // try %HOMEPATH% + TCHAR dir[MAX_PATH]; + DWORD size = sizeof(dir) / sizeof(TCHAR); + DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size); + if (result != 0 && result <= size) { + // sanity check -- if dir doesn't appear to start with a + // drive letter and isn't a UNC name then don't use it + // FIXME -- allow UNC names + if (dir[0] != '\0' && (dir[1] == ':' || + ((dir[0] == '\\' || dir[0] == '/') && + (dir[1] == '\\' || dir[1] == '/')))) { + return dir; + } + } + + // get the location of the personal files. that's as close to + // a home directory as we're likely to find. + ITEMIDLIST* idl; + if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) { + TCHAR* path = NULL; + if (SHGetPathFromIDList(idl, dir)) { + DWORD attr = GetFileAttributes(dir); + if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + path = dir; + } + + IMalloc* shalloc; + if (SUCCEEDED(SHGetMalloc(&shalloc))) { + shalloc->Free(idl); + shalloc->Release(); + } + + if (path != NULL) { + return path; + } + } + + // use root of C drive as a default + return "C:"; +} + +std::string +CArchFileWindows::getSystemDirectory() +{ + // get windows directory + char dir[MAX_PATH]; + if (GetWindowsDirectory(dir, sizeof(dir)) != 0) { + return dir; + } + else { + // can't get it. use C:\ as a default. + return "C:"; + } +} + +std::string +CArchFileWindows::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || + (path[path.size() - 1] != '\\' && + path[path.size() - 1] != '/')) { + path += '\\'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileWindows.h b/lib/arch/CArchFileWindows.h new file mode 100644 index 00000000..44263b38 --- /dev/null +++ b/lib/arch/CArchFileWindows.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEWINDOWS_H +#define CARCHFILEWINDOWS_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileWindows + +class CArchFileWindows : public IArchFile { +public: + CArchFileWindows(); + virtual ~CArchFileWindows(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchImpl.cpp b/lib/arch/CArchImpl.cpp new file mode 100644 index 00000000..c4017122 --- /dev/null +++ b/lib/arch/CArchImpl.cpp @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchMiscWindows.cpp" +# include "CArchConsoleWindows.cpp" +# include "CArchDaemonWindows.cpp" +# include "CArchFileWindows.cpp" +# include "CArchLogWindows.cpp" +# include "CArchMultithreadWindows.cpp" +# include "CArchNetworkWinsock.cpp" +# include "CArchSleepWindows.cpp" +# include "CArchStringWindows.cpp" +# include "CArchTimeWindows.cpp" +# include "XArchWindows.cpp" +#elif UNIX_LIKE +# include "CArchConsoleUnix.cpp" +# include "CArchDaemonUnix.cpp" +# include "CArchFileUnix.cpp" +# include "CArchLogUnix.cpp" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.cpp" +# endif +# include "CArchNetworkBSD.cpp" +# include "CArchSleepUnix.cpp" +# include "CArchStringUnix.cpp" +# include "CArchTimeUnix.cpp" +# include "XArchUnix.cpp" +#endif diff --git a/lib/arch/CArchLogUnix.cpp b/lib/arch/CArchLogUnix.cpp new file mode 100644 index 00000000..c75696e3 --- /dev/null +++ b/lib/arch/CArchLogUnix.cpp @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogUnix.h" +#include + +// +// CArchLogUnix +// + +CArchLogUnix::CArchLogUnix() +{ + // do nothing +} + +CArchLogUnix::~CArchLogUnix() +{ + // do nothing +} + +void +CArchLogUnix::openLog(const char* name) +{ + openlog(name, 0, LOG_DAEMON); +} + +void +CArchLogUnix::closeLog() +{ + closelog(); +} + +void +CArchLogUnix::writeLog(ELevel level, const char* msg) +{ + // convert level + int priority; + switch (level) { + case kERROR: + priority = LOG_ERR; + break; + + case kWARNING: + priority = LOG_WARNING; + break; + + case kNOTE: + priority = LOG_NOTICE; + break; + + case kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} diff --git a/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h new file mode 100644 index 00000000..107d6891 --- /dev/null +++ b/lib/arch/CArchLogUnix.h @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGUNIX_H +#define CARCHLOGUNIX_H + +#include "IArchLog.h" + +#define ARCH_LOG CArchLogUnix + +class CArchLogUnix : public IArchLog { +public: + CArchLogUnix(); + virtual ~CArchLogUnix(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); +}; + +#endif diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp new file mode 100644 index 00000000..a1617da1 --- /dev/null +++ b/lib/arch/CArchLogWindows.cpp @@ -0,0 +1,84 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogWindows.h" +#include "CArchMiscWindows.h" +#include + +// +// CArchLogWindows +// + +CArchLogWindows::CArchLogWindows() +{ + // do nothing +} + +CArchLogWindows::~CArchLogWindows() +{ + // do nothing +} + +void +CArchLogWindows::openLog(const char* name) +{ + if (!CArchMiscWindows::isWindows95Family()) { + m_eventLog = RegisterEventSource(NULL, name); + } +} + +void +CArchLogWindows::closeLog() +{ + if (m_eventLog != NULL) { + DeregisterEventSource(m_eventLog); + m_eventLog = NULL; + } +} + +void +CArchLogWindows::writeLog(ELevel level, const char* msg) +{ + if (m_eventLog != NULL) { + // convert priority + WORD type; + switch (level) { + case kERROR: + type = EVENTLOG_ERROR_TYPE; + break; + + case kWARNING: + type = EVENTLOG_WARNING_TYPE; + break; + + default: + type = EVENTLOG_INFORMATION_TYPE; + break; + } + + // log it + // FIXME -- win32 wants to use a message table to look up event + // strings. log messages aren't organized that way so we'll + // just dump our string into the raw data section of the event + // so users can at least see the message. note that we use our + // level as the event category. + ReportEvent(m_eventLog, type, static_cast(level), + 0, // event ID + NULL, + 0, + strlen(msg + 1), // raw data size + NULL, + const_cast(msg));// raw data + } +} diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h new file mode 100644 index 00000000..1c077643 --- /dev/null +++ b/lib/arch/CArchLogWindows.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGWINDOWS_H +#define CARCHLOGWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchLog.h" +#include + +#define ARCH_LOG CArchLogWindows + +class CArchLogWindows : public IArchLog { +public: + CArchLogWindows(); + virtual ~CArchLogWindows(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + +private: + HANDLE m_eventLog; +}; + +#endif diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp new file mode 100644 index 00000000..25511076 --- /dev/null +++ b/lib/arch/CArchMiscWindows.cpp @@ -0,0 +1,53 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define WIN32_LEAN_AND_MEAN + +#include "CArchMiscWindows.h" +#include "CArchDaemonWindows.h" +#include + +// +// CArchMiscWindows +// + +bool +CArchMiscWindows::isWindows95Family() +{ + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume windows 95 family + return true; + } + return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); +} + +int +CArchMiscWindows::runDaemon(RunFunc runFunc, StopFunc stopFunc) +{ + return CArchDaemonWindows::runDaemon(runFunc, stopFunc); +} + +void +CArchMiscWindows::daemonRunning(bool running) +{ + CArchDaemonWindows::daemonRunning(running); +} + +void +CArchMiscWindows::daemonFailed(int result) +{ + CArchDaemonWindows::daemonFailed(result); +} diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h new file mode 100644 index 00000000..af1a2c78 --- /dev/null +++ b/lib/arch/CArchMiscWindows.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMISCWINDOWS_H +#define CARCHMISCWINDOWS_H + +class CArchMiscWindows { +public: + typedef int (*RunFunc)(void); + typedef void (*StopFunc)(void); + + //! Test if windows 95, et al. + /*! + Returns true iff the platform is win95/98/me. + */ + static bool isWindows95Family(); + + //! Run the daemon + /*! + Delegates to CArchDaemonWindows. + */ + static int runDaemon(RunFunc runFunc, StopFunc stopFunc); + + //! Indicate daemon is in main loop + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonFailed(int result); +}; + +#endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp new file mode 100644 index 00000000..304440b2 --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -0,0 +1,748 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchMultithreadPosix.h" +#include "CArch.h" +#include "XArch.h" +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include + +#define SIGWAKEUP SIGUSR1 + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + +public: + int m_refCount; + pthread_t m_thread; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + bool m_cancel; + bool m_cancelling; + bool m_exited; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_func(NULL), + m_userData(NULL), + m_cancel(false), + m_cancelling(false), + m_exited(false), + m_result(NULL) +{ + // do nothing +} + + +// +// CArchMultithreadPosix +// + +CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; + +CArchMultithreadPosix::CArchMultithreadPosix() : + m_newThreadCalled(false) +{ + assert(s_instance == NULL); + + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + m_mainThread = new CArchThreadImpl; + m_mainThread->m_thread = pthread_self(); + insert(m_mainThread); + + // install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt + // system calls. we use that when cancelling a thread to force it + // to wake up immediately if it's blocked in a system call. we + // won't need this until another thread is created but it's fine + // to install it now. + struct sigaction act; + sigemptyset(&act.sa_mask); +# if defined(SA_INTERRUPT) + act.sa_flags = SA_INTERRUPT; +# else + act.sa_flags = 0; +# endif + act.sa_handler = &threadCancel; + sigaction(SIGWAKEUP, &act, NULL); + + // set desired signal dispositions. let SIGWAKEUP through but + // ignore SIGPIPE (we'll handle EPIPE). + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGWAKEUP); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); +} + +CArchMultithreadPosix::~CArchMultithreadPosix() +{ + assert(s_instance != NULL); + + closeMutex(m_threadMutex); + s_instance = NULL; +} + +CArchCond +CArchMultithreadPosix::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + int status = pthread_cond_init(&cond->m_cond, NULL); + assert(status == 0); + return cond; +} + +void +CArchMultithreadPosix::closeCondVar(CArchCond cond) +{ + int status = pthread_cond_destroy(&cond->m_cond); + assert(status == 0); + delete cond; +} + +void +CArchMultithreadPosix::signalCondVar(CArchCond cond) +{ + int status = pthread_cond_signal(&cond->m_cond); + assert(status == 0); +} + +void +CArchMultithreadPosix::broadcastCondVar(CArchCond cond) +{ + int status = pthread_cond_broadcast(&cond->m_cond); + assert(status == 0); +} + +bool +CArchMultithreadPosix::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // get final time + struct timeval now; + gettimeofday(&now, NULL); + struct timespec finalTime; + finalTime.tv_sec = now.tv_sec; + finalTime.tv_nsec = now.tv_usec * 1000; + if (timeout >= 0.0) { + const long timeout_sec = (long)timeout; + const long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec)); + finalTime.tv_sec += timeout_sec; + finalTime.tv_nsec += timeout_nsec; + if (finalTime.tv_nsec >= 1000000000) { + finalTime.tv_nsec -= 1000000000; + finalTime.tv_sec += 1; + } + } + + // repeat until we reach the final time + int status; + for (;;) { + // get current time + gettimeofday(&now, NULL); + struct timespec endTime; + endTime.tv_sec = now.tv_sec; + endTime.tv_nsec = now.tv_usec * 1000; + + // done if past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + status = ETIMEDOUT; + break; + } + } + + // compute the next timeout + endTime.tv_nsec += 50000000; + if (endTime.tv_nsec >= 1000000000) { + endTime.tv_nsec -= 1000000000; + endTime.tv_sec += 1; + } + + // don't wait past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + endTime = finalTime; + } + } + + // see if we should cancel this thread + testCancelThread(); + + // wait + status = pthread_cond_timedwait(&cond->m_cond, + &mutex->m_mutex, &endTime); + + // check for cancel again + testCancelThread(); + + // check wait status + if (status != ETIMEDOUT && status != EINTR) { + break; + } + } + + switch (status) { + case 0: + // success + return true; + + case ETIMEDOUT: + return false; + + default: + assert(0 && "condition variable wait error"); + return false; + } +} + +CArchMutex +CArchMultithreadPosix::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + int status = pthread_mutex_init(&mutex->m_mutex, NULL); + assert(status == 0); +/* + status = pthread_mutexattr_settype(&mutex->m_mutex, + PTHREAD_MUTEX_RECURSIVE); + assert(status == 0); +*/ + return mutex; +} + +void +CArchMultithreadPosix::closeMutex(CArchMutex mutex) +{ + int status = pthread_mutex_destroy(&mutex->m_mutex); + assert(status == 0); + delete mutex; +} + +void +CArchMultithreadPosix::lockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_lock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EDEADLK: + assert(0 && "lock already owned"); + break; + + case EAGAIN: + assert(0 && "too many recursive locks"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +void +CArchMultithreadPosix::unlockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_unlock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EPERM: + assert(0 && "thread doesn't own a lock"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +CArchThread +CArchMultithreadPosix::newThread(ThreadFunc func, void* data) +{ + assert(func != NULL); + + // initialize signal handler. we do this here instead of the + // constructor so we can avoid daemonizing (using fork()) + // when there are multiple threads. clients can safely + // use condition variables and mutexes before creating a + // new thread and they can safely use the only thread + // they have access to, the main thread, so they really + // can't tell the difference. + if (!m_newThreadCalled) { + m_newThreadCalled = true; + startSignalHandler(); + } + + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // mask some signals in all threads except the main thread + sigset_t sigset, oldsigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); + + // create the thread. pthread_create() on RedHat 7.2 smp fails + // if passed a NULL attr so use a default attr. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&thread->m_thread, &attr, + &CArchMultithreadPosix::threadFunc, thread); + pthread_attr_destroy(&attr); + } + + // restore signals + pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); + + // check if thread was started + if (status != 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadPosix::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(pthread_self()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadPosix::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // detach from thread (unless it's the main thread) + if (thread->m_func != NULL) { + pthread_detach(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRef(thread->m_thread) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadPosix::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadPosix::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel and wakeup flags if thread can be cancelled + bool wakeup = false; + lockMutex(m_threadMutex); + if (!thread->m_exited && !thread->m_cancelling) { + thread->m_cancel = true; + wakeup = true; + } + unlockMutex(m_threadMutex); + + // force thread to exit system calls if wakeup is true + if (wakeup) { + pthread_kill(thread->m_thread, SIGWAKEUP); + } +} + +void +CArchMultithreadPosix::setPriorityOfThread(CArchThread thread, int /*n*/) +{ + assert(thread != NULL); + + // FIXME +} + +void +CArchMultithreadPosix::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(pthread_self()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadPosix::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(pthread_self()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + try { + // do first test regardless of timeout + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // wait and repeat test if there's a timeout + if (timeout != 0.0) { + const double start = ARCH->time(); + do { + // wait a little + ARCH->sleep(0.05); + + // repeat test + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // repeat wait and test until timed out + } while (timeout < 0.0 || (ARCH->time() - start) <= timeout); + } + + closeThread(target); + return false; + } + catch (...) { + closeThread(target); + throw; + } +} + +bool +CArchMultithreadPosix::waitForEvent(double /*timeout*/) +{ + // not implemented +} + +bool +CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadPosix::isExitedThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + bool exited = thread->m_exited; + unlockMutex(m_threadMutex); + return exited; +} + +void* +CArchMultithreadPosix::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadPosix::getIDOfThread(CArchThread thread) +{ + return reinterpret_cast(reinterpret_cast(thread)); +} + +void +CArchMultithreadPosix::startSignalHandler() +{ + // set signal mask. the main thread blocks these signals and + // the signal handler thread will listen for them. + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); + + // fire up the INT and TERM signal handler thread. we could + // instead arrange to catch and handle these signals but + // we'd be unable to cancel the main thread since no pthread + // calls are allowed in a signal handler. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&m_signalThread, &attr, + &CArchMultithreadPosix::threadSignalHandler, + m_mainThread); + pthread_attr_destroy(&attr); + } + if (status != 0) { + // can't create thread to wait for signal so don't block + // the signals. + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + } +} + +CArchThreadImpl* +CArchMultithreadPosix::find(pthread_t thread) +{ + CArchThreadImpl* impl = findNoRef(thread); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadPosix::findNoRef(pthread_t thread) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_thread == thread) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadPosix::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRef(thread->m_thread) == NULL); + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadPosix::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadPosix::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRef(thread->m_thread) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadPosix::testCancelThreadImpl(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = false; + if (thread->m_cancel && !thread->m_cancelling) { + thread->m_cancelling = true; + thread->m_cancel = false; + cancel = true; + } + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +void* +CArchMultithreadPosix::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // setup pthreads + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return NULL; +} + +void +CArchMultithreadPosix::doThreadFunc(CArchThread thread) +{ + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + lockMutex(m_threadMutex); + thread->m_exited = true; + unlockMutex(m_threadMutex); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + thread->m_exited = true; + unlockMutex(m_threadMutex); + + // done with thread + closeThread(thread); +} + +void +CArchMultithreadPosix::threadCancel(int) +{ + // do nothing +} + +void* +CArchMultithreadPosix::threadSignalHandler(void* vrep) +{ + CArchThreadImpl* mainThread = reinterpret_cast(vrep); + + // detach + pthread_detach(pthread_self()); + + // add signal to mask + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + + // also wait on SIGABRT. on linux (others?) this thread (process) + // will persist after all the other threads evaporate due to an + // assert unless we wait on SIGABRT. that means our resources (like + // the socket we're listening on) are not released and never will be + // until the lingering thread is killed. i don't know why sigwait() + // should protect the thread from being killed. note that sigwait() + // doesn't actually return if we receive SIGABRT and, for some + // reason, we don't have to block SIGABRT. + sigaddset(&sigset, SIGABRT); + + // we exit the loop via thread cancellation in sigwait() + for (;;) { + // wait +#if HAVE_POSIX_SIGWAIT + int signal; + sigwait(&sigset, &signal); +#else + sigwait(&sigset); +#endif + + // if we get here then the signal was raised. cancel the main + // thread so it can shut down cleanly. + ARCH->cancelThread(mainThread); + } +} diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h new file mode 100644 index 00000000..cc67a226 --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.h @@ -0,0 +1,93 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADPOSIX_H +#define CARCHMULTITHREADPOSIX_H + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadPosix + +class CArchCondImpl { +public: + pthread_cond_t m_cond; +}; + +class CArchMutexImpl { +public: + pthread_mutex_t m_mutex; +}; + +class CArchMultithreadPosix : public IArchMultithread { +public: + CArchMultithreadPosix(); + virtual ~CArchMultithreadPosix(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual bool waitForEvent(double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + void startSignalHandler(); + + CArchThreadImpl* find(pthread_t thread); + CArchThreadImpl* findNoRef(pthread_t thread); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static void* threadFunc(void* vrep); + static void threadCancel(int); + static void* threadSignalHandler(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadPosix* s_instance; + + bool m_newThreadCalled; + + CArchMutex m_threadMutex; + CArchThread m_mainThread; + CThreadList m_threadList; + + pthread_t m_signalThread; +}; + +#endif diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp new file mode 100644 index 00000000..3a498135 --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -0,0 +1,648 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if !defined(_MT) +# error multithreading compile option is required +#endif + +#include "CArchMultithreadWindows.h" +#include "CArch.h" +#include "XArch.h" +#include + +// +// note -- implementation of condition variable taken from: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// titled "Strategies for Implementing POSIX Condition Variables +// on Win32." it also provides an implementation that doesn't +// suffer from the incorrectness problem described in our +// corresponding header but it is slower, still unfair, and +// can cause busy waiting. +// + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + ~CArchThreadImpl(); + +public: + int m_refCount; + HANDLE m_thread; + DWORD m_id; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + HANDLE m_cancel; + bool m_cancelling; + HANDLE m_exit; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_thread(NULL), + m_id(0), + m_func(NULL), + m_userData(NULL), + m_cancelling(false), + m_result(NULL) +{ + m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); + m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +CArchThreadImpl::~CArchThreadImpl() +{ + CloseHandle(m_exit); + CloseHandle(m_cancel); +} + + +// +// CArchMultithreadWindows +// + +CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; + +CArchMultithreadWindows::CArchMultithreadWindows() +{ + assert(s_instance == NULL); + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + CArchThreadImpl* mainThread = new CArchThreadImpl; + mainThread->m_thread = NULL; + mainThread->m_id = GetCurrentThreadId(); + insert(mainThread); +} + +CArchMultithreadWindows::~CArchMultithreadWindows() +{ + s_instance = NULL; +} + +HANDLE +CArchMultithreadWindows::getCancelEventForCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + return thread->m_cancel; +} + +CArchMultithreadWindows* +CArchMultithreadWindows::getInstance() +{ + return s_instance; +} + +CArchCond +CArchMultithreadWindows::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + cond->m_events[CArchCondImpl::kSignal] = CreateEvent(NULL, + FALSE, FALSE, NULL); + cond->m_events[CArchCondImpl::kBroadcast] = CreateEvent(NULL, + TRUE, FALSE, NULL); + cond->m_waitCountMutex = newMutex(); + cond->m_waitCount = 0; + return cond; +} + +void +CArchMultithreadWindows::closeCondVar(CArchCond cond) +{ + CloseHandle(cond->m_events[CArchCondImpl::kSignal]); + CloseHandle(cond->m_events[CArchCondImpl::kBroadcast]); + closeMutex(cond->m_waitCountMutex); + delete cond; +} + +void +CArchMultithreadWindows::signalCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake one thread if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kSignal]); + } +} + +void +CArchMultithreadWindows::broadcastCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake all threads if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } +} + +bool +CArchMultithreadWindows::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // prepare to wait + const DWORD winTimeout = (timeout < 0.0) ? INFINITE : + static_cast(1000.0 * timeout); + + // make a list of the condition variable events and the cancel event + // for the current thread. + HANDLE handles[3]; + handles[0] = cond->m_events[CArchCondImpl::kSignal]; + handles[1] = cond->m_events[CArchCondImpl::kBroadcast]; + handles[2] = getCancelEventForCurrentThread(); + + // update waiter count + lockMutex(cond->m_waitCountMutex); + ++cond->m_waitCount; + unlockMutex(cond->m_waitCountMutex); + + // release mutex. this should be atomic with the wait so that it's + // impossible for another thread to signal us between the unlock and + // the wait, which would lead to a lost signal on broadcasts. + // however, we're using a manual reset event for broadcasts which + // stays set until we reset it, so we don't lose the broadcast. + unlockMutex(mutex); + + // wait for a signal or broadcast + DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 2 && + WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 2; + } + + // update the waiter count and check if we're the last waiter + lockMutex(cond->m_waitCountMutex); + --cond->m_waitCount; + const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0); + unlockMutex(cond->m_waitCountMutex); + + // reset the broadcast event if we're the last waiter + if (last) { + ResetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } + + // reacquire the mutex + lockMutex(mutex); + + // cancel thread if necessary + if (result == WAIT_OBJECT_0 + 2) { + ARCH->testCancelThread(); + } + + // return success or failure + return (result == WAIT_OBJECT_0 + 0 || + result == WAIT_OBJECT_0 + 1); +} + +CArchMutex +CArchMultithreadWindows::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + InitializeCriticalSection(&mutex->m_mutex); + return mutex; +} + +void +CArchMultithreadWindows::closeMutex(CArchMutex mutex) +{ + DeleteCriticalSection(&mutex->m_mutex); + delete mutex; +} + +void +CArchMultithreadWindows::lockMutex(CArchMutex mutex) +{ + EnterCriticalSection(&mutex->m_mutex); +} + +void +CArchMultithreadWindows::unlockMutex(CArchMutex mutex) +{ + LeaveCriticalSection(&mutex->m_mutex); +} + +CArchThread +CArchMultithreadWindows::newThread(ThreadFunc func, void* data) +{ + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // create thread + unsigned int id; + thread->m_thread = reinterpret_cast(_beginthreadex(NULL, 0, + threadFunc, (void*)thread, 0, &id)); + thread->m_id = static_cast(id); + + // check if thread was started + if (thread->m_thread == 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadWindows::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadWindows::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // close the handle (main thread has a NULL handle) + if (thread->m_thread != NULL) { + CloseHandle(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRef(thread->m_id) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadWindows::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadWindows::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel flag + SetEvent(thread->m_cancel); +} + +void +CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) +{ + assert(thread != NULL); + + DWORD pClass = NORMAL_PRIORITY_CLASS; + if (n < 0) { + switch (-n) { + case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; + case 2: n = THREAD_PRIORITY_HIGHEST; break; + default: + pClass = HIGH_PRIORITY_CLASS; + switch (-n - 3) { + case 0: n = THREAD_PRIORITY_LOWEST; break; + case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; + case 2: n = THREAD_PRIORITY_NORMAL; break; + case 3: n = THREAD_PRIORITY_ABOVE_NORMAL; break; + default: n = THREAD_PRIORITY_HIGHEST; break; + } + break; + } + } + else { + switch (n) { + case 0: n = THREAD_PRIORITY_NORMAL; break; + case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; + case 2: n = THREAD_PRIORITY_LOWEST; break; + default: n = THREAD_PRIORITY_IDLE; break; + } + } + SetPriorityClass(thread->m_thread, pClass); + SetThreadPriority(thread->m_thread, n); +} + +void +CArchMultithreadWindows::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadWindows::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + HANDLE handles[2]; + handles[0] = target->m_exit; + handles[1] = self->m_cancel; + DWORD result = WaitForMultipleObjects(2, handles, FALSE, t); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 1 && + WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 1; + } + + // release target + closeThread(target); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // target thread terminated + return true; + + case WAIT_OBJECT_0 + 1: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} + +bool +CArchMultithreadWindows::waitForEvent(double timeout) +{ + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return true; + } + + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(self != NULL); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for a message + HANDLE handles[1]; + handles[0] = self->m_cancel; + DWORD result = MsgWaitForMultipleObjects(1, handles, FALSE, t, QS_ALLINPUT); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 1: + // message is available + return true; + + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} + +bool +CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadWindows::isExitedThread(CArchThread thread) +{ + // poll exit event + return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0); +} + +void* +CArchMultithreadWindows::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadWindows::getIDOfThread(CArchThread thread) +{ + return static_cast(thread->m_id); +} + +CArchThreadImpl* +CArchMultithreadWindows::find(DWORD id) +{ + CArchThreadImpl* impl = findNoRef(id); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadWindows::findNoRef(DWORD id) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_id == id) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadWindows::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRef(thread->m_id) == NULL); + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadWindows::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadWindows::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRef(thread->m_id) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadWindows::testCancelThreadImpl(CArchThread thread) +{ + assert(thread != NULL); + + // poll cancel event. return if not set. + const DWORD result = WaitForSingleObject(thread->m_cancel, 0); + if (result != WAIT_OBJECT_0) { + return; + } + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = !thread->m_cancelling; + thread->m_cancelling = true; + ResetEvent(thread->m_cancel); + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +unsigned int __stdcall +CArchMultithreadWindows::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return 0; +} + +void +CArchMultithreadWindows::doThreadFunc(CArchThread thread) +{ + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + SetEvent(thread->m_exit); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + unlockMutex(m_threadMutex); + SetEvent(thread->m_exit); + + // done with thread + closeThread(thread); +} diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h new file mode 100644 index 00000000..74b2d36b --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.h @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADWINDOWS_H +#define CARCHMULTITHREADWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadWindows + +class CArchCondImpl { +public: + enum { kSignal = 0, kBroadcast }; + + HANDLE m_events[2]; + mutable int m_waitCount; + CArchMutex m_waitCountMutex; +}; + +class CArchMutexImpl { +public: + CRITICAL_SECTION m_mutex; +}; + +class CArchMultithreadWindows : public IArchMultithread { +public: + CArchMultithreadWindows(); + virtual ~CArchMultithreadWindows(); + + // + // accessors + // + + HANDLE getCancelEventForCurrentThread(); + + static CArchMultithreadWindows* getInstance(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual bool waitForEvent(double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + CArchThreadImpl* find(DWORD id); + CArchThreadImpl* findNoRef(DWORD id); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static unsigned int __stdcall threadFunc(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadWindows* s_instance; + + CArchMutex m_threadMutex; + + CThreadList m_threadList; +}; + +#endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp new file mode 100644 index 00000000..8e3f4706 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.cpp @@ -0,0 +1,847 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchNetworkBSD.h" +#include "CArch.h" +#include "XArchUnix.h" +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif +#include +#include +#if !defined(TCP_NODELAY) +# include +#endif +#include +#include +#include + +#if HAVE_POLL +# include +#else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +#endif + +static const int s_family[] = { + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +// +// CArchNetworkBSD +// + +CArchNetworkBSD::CArchNetworkBSD() +{ + // create mutex to make some calls thread safe + m_mutex = ARCH->newMutex(); +} + +CArchNetworkBSD::~CArchNetworkBSD() +{ + ARCH->closeMutex(m_mutex); +} + +CArchSocket +CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* newSocket = new CArchSocketImpl; + + // create socket + int fd = socket(s_family[family], s_type[type], 0); + if (fd == -1) { + throwError(errno); + } + + newSocket->m_fd = fd; + newSocket->m_connected = false; + newSocket->m_refCount = 1; + return newSocket; +} + +CArchSocket +CArchNetworkBSD::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkBSD::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close(s->m_fd) == -1) { + // close failed + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkBSD::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 0) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 1) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + throwError(errno); + } +} + +void +CArchNetworkBSD::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen(s->m_fd, 3) == -1) { + throwError(errno); + } +} + +CArchSocket +CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* newSocket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + int fd; + do { + fd = accept(s->m_fd, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == -1) { + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == ECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete newSocket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + newSocket->m_fd = fd; + newSocket->m_connected = true; + newSocket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return newSocket; +} + +void +CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (errno == EISCONN) { + // already connected + break; + } + + if (errno == EAGAIN) { + // connecting + throw XArchNetworkConnecting(new XArchEvalUnix(errno)); + } + + throwError(errno); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +#if HAVE_POLL + +int +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + assert(pe != NULL || num == 0); + + // return if nothing to do + if (num == 0) { + if (timeout > 0.0) { + ARCH->sleep(timeout); + } + return 0; + } + + // allocate space for translated query + struct pollfd* pfd = new struct pollfd[num]; + + // translate query + for (int i = 0; i < num; ++i) { + pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd; + pfd[i].events = 0; + if ((pe[i].m_events & kPOLLIN) != 0) { + pfd[i].events |= POLLIN; + } + if ((pe[i].m_events & kPOLLOUT) != 0) { + pfd[i].events |= POLLOUT; + } + } + + // do the poll + int n; + do { + n = poll(pfd, num, static_cast(1000.0 * timeout)); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + delete[] pfd; + throwError(errno); + } + } while (false); + + // translate back + for (int i = 0; i < num; ++i) { + pe[i].m_revents = 0; + if ((pfd[i].revents & POLLIN) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((pfd[i].revents & POLLOUT) != 0) { + pe[i].m_revents |= kPOLLOUT; + } + if ((pfd[i].revents & POLLERR) != 0) { + pe[i].m_revents |= kPOLLERR; + } + if ((pfd[i].revents & POLLNVAL) != 0) { + pe[i].m_revents |= kPOLLNVAL; + } + } + + // done with translated query + delete[] pfd; + + return n; +} + +#else + +void +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + int fdi = pe[i].m_socket->m_fd; + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_fd, &readSet); + readSetP = &readSet; + if (fdi > n) { + n = fdi; + } + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_fd, &writeSet); + writeSetP = &writeSet; + if (fdi > n) { + n = fdi; + } + } + if (true) { + FD_SET(pe[i].m_socket->m_fd, &errSet); + errSetP = &errSet; + if (fdi > n) { + n = fdi; + } + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +#endif + +size_t +CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = read(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +size_t +CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = write(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +void +CArchNetworkBSD::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + socklen_t size = sizeof(err); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, &err, &size) == -1) { + err = errno; + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkBSD::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int mode = fcntl(s->m_fd, F_GETFL, 0); + if (mode == -1) { + throwError(errno); + } + bool old = ((mode & O_NDELAY) == 0); + if (blocking) { + mode &= ~O_NDELAY; + } + else { + mode |= O_NDELAY; + } + if (fcntl(s->m_fd, F_SETFL, mode) == -1) { + throwError(errno); + } + return old; +} + +bool +CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = sizeof(oflag); + if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &oflag, &size) == -1) { + throwError(errno); + } + + int flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + +std::string +CArchNetworkBSD::getHostName() +{ + char name[256]; + if (gethostname(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkBSD::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkBSD::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkBSD::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + if (inet_aton(name.c_str(), &inaddr.sin_addr) != 0) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // mutexed address lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyname(name.c_str()); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + delete addr; + throwNameError(h_errno); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + + // done with static buffer + ARCH->unlockMutex(m_mutex); + } + + return addr; +} + +void +CArchNetworkBSD::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkBSD::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // mutexed name lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyaddr(&addr->m_addr, addr->m_len, + addr->m_addr.sa_family); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + throwNameError(h_errno); + } + + // save (primary) name + std::string name = info->h_name; + + // done with static buffer + ARCH->unlockMutex(m_mutex); + + return name; +} + +std::string +CArchNetworkBSD::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ARCH->lockMutex(m_mutex); + std::string s = inet_ntoa(ipAddr->sin_addr); + ARCH->unlockMutex(m_mutex); + return s; + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkBSD::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkBSD::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons(port); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkBSD::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkBSD::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkBSD::throwError(int err) +{ + switch (err) { + case EAGAIN: + throw XArchNetworkWouldBlock(new XArchEvalUnix(err)); + + case EACCES: + case EPERM: + throw XArchNetworkAccess(new XArchEvalUnix(err)); + + case ENFILE: + case EMFILE: + case ENODEV: + case ENOBUFS: + case ENOMEM: + case ENOSR: + case ENETDOWN: + throw XArchNetworkResource(new XArchEvalUnix(err)); + + case EPROTOTYPE: + case EPROTONOSUPPORT: + case EAFNOSUPPORT: + case EPFNOSUPPORT: + case ESOCKTNOSUPPORT: + case EINVAL: + case ENOPROTOOPT: + case EOPNOTSUPP: + case ENOPKG: + case ESHUTDOWN: + throw XArchNetworkSupport(new XArchEvalUnix(err)); + + case EIO: + throw XArchNetworkIO(new XArchEvalUnix(err)); + + case EADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalUnix(err)); + + case EADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalUnix(err)); + + case EHOSTUNREACH: + case ENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalUnix(err)); + + case ENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalUnix(err)); + + case EPIPE: + case ECONNABORTED: + case ECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalUnix(err)); + + case ECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalUnix(err)); + + case EINPROGRESS: + case EALREADY: + throw XArchNetworkConnecting(new XArchEvalUnix(err)); + + case EHOSTDOWN: + case ETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalUnix(err)); + + default: + throw XArchNetwork(new XArchEvalUnix(err)); + } +} + +void +CArchNetworkBSD::throwNameError(int err) +{ + static const char* s_msg[] = { + "The specified host is unknown", + "The requested name is valid but does not have an IP address", + "A non-recoverable name server error occurred", + "A temporary error occurred on an authoritative name server", + "An unknown name server error occurred" + }; + + switch (err) { + case HOST_NOT_FOUND: + throw XArchNetworkNameUnknown(s_msg[0]); + + case NO_DATA: + throw XArchNetworkNameNoAddress(s_msg[1]); + + case NO_RECOVERY: + throw XArchNetworkNameFailure(s_msg[2]); + + case TRY_AGAIN: + throw XArchNetworkNameUnavailable(s_msg[3]); + + default: + throw XArchNetworkName(s_msg[4]); + } +} diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h new file mode 100644 index 00000000..2d2eda76 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.h @@ -0,0 +1,95 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKBSD_H +#define CARCHNETWORKBSD_H + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#if HAVE_SYS_SOCKET_H +# include +#endif + +#if !defined(HAVE_SOCKLEN_T) +// Darwin is so unsure what to use for socklen_t it makes us choose +# if defined(__APPLE__) +# if !defined(_BSD_SOCKLEN_T_) +# define _BSD_SOCKLEN_T_ int +# endif +# else +typedef int socklen_t; +# endif +#endif + +#define ARCH_NETWORK CArchNetworkBSD + +class CArchSocketImpl { +public: + int m_fd; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + socklen_t m_len; +}; + +class CArchNetworkBSD : public IArchNetwork { +public: + CArchNetworkBSD(); + virtual ~CArchNetworkBSD(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp new file mode 100644 index 00000000..10e40188 --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -0,0 +1,838 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include "CArchNetworkWinsock.h" +#include "CArch.h" +#include "XArchWindows.h" + +static const int s_family[] = { + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +static SOCKET (PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen); +static int (PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen); +static int (PASCAL FAR *close_winsock)(SOCKET s); +static int (PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen); +static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen); +static int (PASCAL FAR *getsockerror_winsock)(void); +static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen); +static u_short (PASCAL FAR *htons_winsock)(u_short v); +static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); +static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); +static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR *); +static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog); +static u_short (PASCAL FAR *ntohs_winsock)(u_short v); +static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags); +static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); +static int (PASCAL FAR *send_winsock)(SOCKET s, const void FAR * buf, int len, int flags); +static int (PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR * optval, int optlen); +static int (PASCAL FAR *shutdown_winsock)(SOCKET s, int how); +static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol); +static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); +static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); +static int (PASCAL FAR *WSACleanup_winsock)(void); +static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR *); + +#undef FD_ISSET +#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) + +#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) + +static HMODULE s_networkModule = NULL; + +static +FARPROC +netGetProcAddress(HMODULE module, LPCSTR name) +{ + FARPROC func = ::GetProcAddress(module, name); + if (!func) { + throw XArchNetworkSupport(""); + } + return func; +} + +// +// CArchNetworkWinsock +// + +CArchNetworkWinsock::CArchNetworkWinsock() +{ + static const char* s_library[] = { "ws2_32.dll", "wsock32.dll" }; + + assert(WSACleanup_winsock == NULL); + assert(s_networkModule == NULL); + + // try each winsock library + for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) { + try { + init((HMODULE)::LoadLibrary(s_library[i])); + m_mutex = ARCH->newMutex(); + return; + } + catch (XArchNetwork&) { + // ignore + } + } + + // can't initialize any library + throw XArchNetworkSupport("Cannot load winsock library"); +} + +CArchNetworkWinsock::~CArchNetworkWinsock() +{ + if (s_networkModule != NULL) { + WSACleanup_winsock(); + ::FreeLibrary(s_networkModule); + + WSACleanup_winsock = NULL; + s_networkModule = NULL; + } + ARCH->closeMutex(m_mutex); +} + +void +CArchNetworkWinsock::init(HMODULE module) +{ + assert(module != NULL); + + // get startup function address + int (PASCAL FAR *startup)(WORD, LPWSADATA); + setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); + + // startup network library + WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); + WSADATA data; + int err = startup(version, &data); + if (data.wVersion != version) { + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + } + if (err != 0) { + // some other initialization error + throwError(err); + } + + // get function addresses + setfunc(accept_winsock, accept, SOCKET (PASCAL FAR *)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)); + setfunc(bind_winsock, bind, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen)); + setfunc(close_winsock, closesocket, int (PASCAL FAR *)(SOCKET s)); + setfunc(connect_winsock, connect, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen)); + setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen)); + setfunc(htons_winsock, htons, u_short (PASCAL FAR *)(u_short v)); + setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(SOCKET s, int cmd, void FAR *)); + setfunc(listen_winsock, listen, int (PASCAL FAR *)(SOCKET s, int backlog)); + setfunc(ntohs_winsock, ntohs, u_short (PASCAL FAR *)(u_short v)); + setfunc(recv_winsock, recv, int (PASCAL FAR *)(SOCKET s, void FAR * buf, int len, int flags)); + setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); + setfunc(send_winsock, send, int (PASCAL FAR *)(SOCKET s, const void FAR * buf, int len, int flags)); + setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR * optval, int optlen)); + setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(SOCKET s, int how)); + setfunc(socket_winsock, socket, SOCKET (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); + setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *)); + + s_networkModule = module; +} + +CArchSocket +CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* socket = new CArchSocketImpl; + + // create socket + SOCKET fd = socket_winsock(s_family[family], s_type[type], 0); + if (fd == INVALID_SOCKET) { + throwError(getsockerror_winsock()); + } + + socket->m_socket = fd; + socket->m_connected = false; + socket->m_refCount = 1; + return socket; +} + +CArchSocket +CArchNetworkWinsock::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkWinsock::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close_winsock(s->m_socket) == SOCKET_ERROR) { + // close failed + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkWinsock::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +void +CArchNetworkWinsock::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +CArchSocket +CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* socket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + SOCKET fd; + do { + fd = accept_winsock(s->m_socket, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == INVALID_SOCKET) { + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == WSAECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete socket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + socket->m_socket = fd; + socket->m_connected = true; + socket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return socket; +} + +void +CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect_winsock(s->m_socket, &addr->m_addr, + addr->m_len) == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (getsockerror_winsock() == WSAEISCONN) { + // already connected + break; + } + + if (getsockerror_winsock() == WSAEWOULDBLOCK) { + // connecting + throw XArchNetworkConnecting(new XArchEvalWinsock( + getsockerror_winsock())); + } + + throwError(getsockerror_winsock()); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +int +CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_socket, &readSet); + readSetP = &readSet; + n = 1; + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_socket, &writeSet); + writeSetP = &writeSet; + n = 1; + } + if (true) { + FD_SET(pe[i].m_socket->m_socket, &errSet); + errSetP = &errSet; + n = 1; + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select_winsock(0, readSetP, writeSetP, errSetP, timeout2P); + + // handle results + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_socket, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +size_t +CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = recv_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +size_t +CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = send_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +void +CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + int size = sizeof(err); + if (getsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_ERROR, &err, &size) == SOCKET_ERROR) { + err = getsockerror_winsock(); + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkWinsock::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int flag = blocking ? 0 : 1; + if (ioctl_winsock(s->m_socket, FIONBIO, &flag) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + // FIXME -- can't get the current blocking state of socket? + return true; +} + +bool +CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + BOOL oflag; + int size = sizeof(oflag); + if (getsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + // set new state + BOOL flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &flag, size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + return (oflag != 0); +} + +std::string +CArchNetworkWinsock::getHostName() +{ + char name[256]; + if (gethostname_winsock(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkWinsock::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkWinsock::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkWinsock::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str()); + if (inaddr.sin_addr.s_addr != INADDR_NONE) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // address lookup + struct hostent* info = gethostbyname_winsock(name.c_str()); + if (info == NULL) { + delete addr; + throwNameError(getsockerror_winsock()); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + return addr; +} + +void +CArchNetworkWinsock::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkWinsock::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // name lookup + struct hostent* info = gethostbyaddr_winsock( + reinterpret_cast(&addr->m_addr), + addr->m_len, addr->m_addr.sa_family); + if (info == NULL) { + throwNameError(getsockerror_winsock()); + } + + // return (primary) name + return info->h_name; +} + +std::string +CArchNetworkWinsock::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return inet_ntoa_winsock(ipAddr->sin_addr); + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkWinsock::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkWinsock::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons_winsock(static_cast(port)); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkWinsock::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs_winsock(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkWinsock::throwError(int err) +{ + switch (err) { + case WSAEWOULDBLOCK: + throw XArchNetworkWouldBlock(new XArchEvalWinsock(err)); + + case WSAEACCES: + throw XArchNetworkAccess(new XArchEvalWinsock(err)); + + case WSAEMFILE: + case WSAENOBUFS: + case WSAENETDOWN: + throw XArchNetworkResource(new XArchEvalWinsock(err)); + + case WSAEPROTOTYPE: + case WSAEPROTONOSUPPORT: + case WSAEAFNOSUPPORT: + case WSAEPFNOSUPPORT: + case WSAESOCKTNOSUPPORT: + case WSAEINVAL: + case WSAENOPROTOOPT: + case WSAEOPNOTSUPP: + case WSAESHUTDOWN: + case WSANOTINITIALISED: + case WSAVERNOTSUPPORTED: + case WSASYSNOTREADY: + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + + case WSAEADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalWinsock(err)); + + case WSAEADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalWinsock(err)); + + case WSAEHOSTUNREACH: + case WSAENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalWinsock(err)); + + case WSAENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalWinsock(err)); + + case WSAENETRESET: + case WSAEDISCON: + case WSAECONNABORTED: + case WSAECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalWinsock(err)); + + case WSAECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err)); + + case WSAEINPROGRESS: + case WSAEALREADY: + throw XArchNetworkConnecting(new XArchEvalWinsock(err)); + + case WSAEHOSTDOWN: + case WSAETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalWinsock(err)); + + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetwork(new XArchEvalWinsock(err)); + } +} + +void +CArchNetworkWinsock::throwNameError(int err) +{ + switch (err) { + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetworkName(new XArchEvalWinsock(err)); + } +} diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h new file mode 100644 index 00000000..d16b7f48 --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.h @@ -0,0 +1,91 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKWINSOCK_H +#define CARCHNETWORKWINSOCK_H + +#define WIN32_LEAN_AND_MEAN + +// declare no functions in winsock2 +#define INCL_WINSOCK_API_PROTOTYPES 0 +#define INCL_WINSOCK_API_TYPEDEFS 0 + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#include +#include + +#define ARCH_NETWORK CArchNetworkWinsock + +class CArchSocketImpl { +public: + SOCKET m_socket; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + int m_len; +}; + +class CArchNetworkWinsock : public IArchNetwork { +public: + CArchNetworkWinsock(); + virtual ~CArchNetworkWinsock(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void init(HMODULE); + + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchSleepUnix.cpp b/lib/arch/CArchSleepUnix.cpp new file mode 100644 index 00000000..1e5675f4 --- /dev/null +++ b/lib/arch/CArchSleepUnix.cpp @@ -0,0 +1,88 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepUnix.h" +#include "CArch.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#if !HAVE_NANOSLEEP +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +#endif + +// +// CArchSleepUnix +// + +CArchSleepUnix::CArchSleepUnix() +{ + // do nothing +} + +CArchSleepUnix::~CArchSleepUnix() +{ + // do nothing +} + +void +CArchSleepUnix::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + +#if HAVE_NANOSLEEP + // prep timeout + struct timespec t; + t.tv_sec = (long)timeout; + t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec)); + + // wait + while (nanosleep(&t, &t) < 0) + ARCH->testCancelThread(); +#else + /* emulate nanosleep() with select() */ + double startTime = time(); + double timeLeft = timeout; + while (timeLeft > 0.0) { + struct timeval timeout2; + timeout2.tv_sec = static_cast(timeLeft); + timeout2.tv_usec = static_cast(1.0e+6 * (timeLeft - + timeout2.tv_sec)); + select((SELECT_TYPE_ARG1) 0, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 &timeout2); + ARCH->testCancelThread(); + timeLeft = timeout - (time() - startTime); + } +#endif +} diff --git a/lib/arch/CArchSleepUnix.h b/lib/arch/CArchSleepUnix.h new file mode 100644 index 00000000..deb33629 --- /dev/null +++ b/lib/arch/CArchSleepUnix.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPUNIX_H +#define CARCHSLEEPUNIX_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepUnix + +class CArchSleepUnix : public IArchSleep { +public: + CArchSleepUnix(); + virtual ~CArchSleepUnix(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/arch/CArchSleepWindows.cpp b/lib/arch/CArchSleepWindows.cpp new file mode 100644 index 00000000..9c0fe596 --- /dev/null +++ b/lib/arch/CArchSleepWindows.cpp @@ -0,0 +1,54 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepWindows.h" +#include "CArch.h" +#include "CArchMultithreadWindows.h" + +// +// CArchSleepWindows +// + +CArchSleepWindows::CArchSleepWindows() +{ + // do nothing +} + +CArchSleepWindows::~CArchSleepWindows() +{ + // do nothing +} + +void +CArchSleepWindows::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + + // get the cancel event from the current thread. this only + // works if we're using the windows multithread object but + // this is windows so that's pretty certain; we'll get a + // link error if we're not, though. + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + if (mt != NULL) { + HANDLE cancelEvent = mt->getCancelEventForCurrentThread(); + WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout)); + } + else { + Sleep((DWORD)(1000.0 * timeout)); + } + ARCH->testCancelThread(); +} diff --git a/lib/arch/CArchSleepWindows.h b/lib/arch/CArchSleepWindows.h new file mode 100644 index 00000000..a142d997 --- /dev/null +++ b/lib/arch/CArchSleepWindows.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPWINDOWS_H +#define CARCHSLEEPWINDOWS_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepWindows + +class CArchSleepWindows : public IArchSleep { +public: + CArchSleepWindows(); + virtual ~CArchSleepWindows(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/platform/CPlatform.h b/lib/arch/CArchStringUnix.cpp similarity index 67% rename from lib/platform/CPlatform.h rename to lib/arch/CArchStringUnix.cpp index dcfb6b02..385bb0aa 100644 --- a/lib/platform/CPlatform.h +++ b/lib/arch/CArchStringUnix.cpp @@ -12,25 +12,23 @@ * GNU General Public License for more details. */ -#ifndef CPLATFORM_H -#define CPLATFORM_H +#include "CArchStringUnix.h" +#include -#include "common.h" +#include "CMultibyte.cpp" -#if WINDOWS_LIKE +// +// CArchStringUnix +// -#include "CWin32Platform.h" -typedef CWin32Platform CPlatform; +CArchStringUnix::CArchStringUnix() +{ + initMB(); +} -#elif UNIX_LIKE +CArchStringUnix::~CArchStringUnix() +{ + cleanMB(); +} -#include "CUnixPlatform.h" -typedef CUnixPlatform CPlatform; - -#else - -#error Unsupported platform - -#endif - -#endif +#include "vsnprintf.cpp" diff --git a/lib/arch/CArchStringUnix.h b/lib/arch/CArchStringUnix.h new file mode 100644 index 00000000..fcd114f9 --- /dev/null +++ b/lib/arch/CArchStringUnix.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGUNIX_H +#define CARCHSTRINGUNIX_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringUnix + +class CArchStringUnix : public IArchString { +public: + CArchStringUnix(); + virtual ~CArchStringUnix(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); +}; + +#endif diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp new file mode 100644 index 00000000..a0d8727d --- /dev/null +++ b/lib/arch/CArchStringWindows.cpp @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define WIN32_LEAN_AND_MEAN + +#include "CArchStringWindows.h" +#include + +#include "CMultibyte.cpp" + +// +// CArchStringWindows +// + +CArchStringWindows::CArchStringWindows() +{ + initMB(); +} + +CArchStringWindows::~CArchStringWindows() +{ + cleanMB(); +} + +#define HAVE_VSNPRINTF 1 +#define ARCH_VSNPRINTF _vsnprintf +#include "vsnprintf.cpp" diff --git a/lib/arch/CArchStringWindows.h b/lib/arch/CArchStringWindows.h new file mode 100644 index 00000000..54b77ca3 --- /dev/null +++ b/lib/arch/CArchStringWindows.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGWINDOWS_H +#define CARCHSTRINGWINDOWS_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringWindows + +class CArchStringWindows : public IArchString { +public: + CArchStringWindows(); + virtual ~CArchStringWindows(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); +}; + +#endif diff --git a/lib/arch/CArchTimeUnix.cpp b/lib/arch/CArchTimeUnix.cpp new file mode 100644 index 00000000..49506bad --- /dev/null +++ b/lib/arch/CArchTimeUnix.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTimeUnix.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +// +// CArchTimeUnix +// + +CArchTimeUnix::CArchTimeUnix() +{ + // do nothing +} + +CArchTimeUnix::~CArchTimeUnix() +{ + // do nothing +} + +double +CArchTimeUnix::time() +{ + struct timeval t; + gettimeofday(&t, NULL); + return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; +} diff --git a/lib/arch/CArchTimeUnix.h b/lib/arch/CArchTimeUnix.h new file mode 100644 index 00000000..c9b99999 --- /dev/null +++ b/lib/arch/CArchTimeUnix.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEUNIX_H +#define CARCHTIMEUNIX_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeUnix + +class CArchTimeUnix : public IArchTime { +public: + CArchTimeUnix(); + virtual ~CArchTimeUnix(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CArchTimeWindows.cpp b/lib/arch/CArchTimeWindows.cpp new file mode 100644 index 00000000..57aee290 --- /dev/null +++ b/lib/arch/CArchTimeWindows.cpp @@ -0,0 +1,86 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +// avoid getting a lot a crap from mmsystem.h that we don't need +#define MMNODRV // Installable driver support +#define MMNOSOUND // Sound support +#define MMNOWAVE // Waveform support +#define MMNOMIDI // MIDI support +#define MMNOAUX // Auxiliary audio support +#define MMNOMIXER // Mixer support +#define MMNOJOY // Joystick support +#define MMNOMCI // MCI support +#define MMNOMMIO // Multimedia file I/O support +#define MMNOMMSYSTEM // General MMSYSTEM functions + +#define WIN32_LEAN_AND_MEAN + +#include "CArchTimeWindows.h" +#include +#include + +typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void); + +static double s_freq = 0.0; +static HINSTANCE s_mmInstance = NULL; +static PTimeGetTime s_tgt = NULL; + + +// +// CArchTimeWindows +// + +CArchTimeWindows::CArchTimeWindows() +{ + assert(s_freq == 0.0 || s_mmInstance == NULL); + + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) { + s_freq = 1.0 / static_cast(freq.QuadPart); + } + else { + // load winmm.dll and get timeGetTime + s_mmInstance = LoadLibrary("winmm"); + if (s_mmInstance != NULL) { + s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); + } + } +} + +CArchTimeWindows::~CArchTimeWindows() +{ + s_freq = 0.0; + if (s_mmInstance == NULL) { + FreeLibrary(reinterpret_cast(s_mmInstance)); + s_tgt = NULL; + s_mmInstance = NULL; + } +} + +double +CArchTimeWindows::time() +{ + // get time. we try three ways, in order of descending precision + if (s_freq != 0.0) { + LARGE_INTEGER c; + QueryPerformanceCounter(&c); + return s_freq * static_cast(c.QuadPart); + } + else if (s_tgt != NULL) { + return 0.001 * static_cast(s_tgt()); + } + else { + return 0.001 * static_cast(GetTickCount()); + } +} diff --git a/lib/arch/CArchTimeWindows.h b/lib/arch/CArchTimeWindows.h new file mode 100644 index 00000000..0672ad78 --- /dev/null +++ b/lib/arch/CArchTimeWindows.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEWINDOWS_H +#define CARCHTIMEWINDOWS_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeWindows + +class CArchTimeWindows : public IArchTime { +public: + CArchTimeWindows(); + virtual ~CArchTimeWindows(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp new file mode 100644 index 00000000..43b8b41f --- /dev/null +++ b/lib/arch/CMultibyte.cpp @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMULTIBYTE_H +#define CMULTIBYTE_H + +#include "common.h" + +#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || WINDOWS_LIKE +#include "CMultibyteOS.cpp" +#else +#include "CMultibyteEmu.cpp" +#endif + +CArchMBState +ARCH_STRING::newMBState() +{ + CArchMBState state = new CArchMBStateImpl; + initMBState(state); + return state; +} + +void +ARCH_STRING::closeMBState(CArchMBState state) +{ + delete state; +} + +#endif diff --git a/lib/arch/CMultibyteEmu.cpp b/lib/arch/CMultibyteEmu.cpp new file mode 100644 index 00000000..80569fe5 --- /dev/null +++ b/lib/arch/CMultibyteEmu.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArch.h" +#include +#include + +class CArchMBStateImpl { +public: + mbstate_t m_mbstate; +}; + +// +// use C library non-reentrant multibyte conversion with mutex +// + +static CArchMutex s_mutex; + +static +void +initMB() +{ + s_mutex = ARCH->newMutex(); +} + +static +void +cleanMB() +{ + ARCH->closeMutex(s_mutex); +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ +#if !HAVE_MBSINIT + return (mbsinit(&state->m_mbstate) != 0); +#else + return true; +#endif +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState) +{ + wchar_t dummy; + ARCH->lockMutex(s_mutex); + int result = mbtowc(dst != NULL ? dst : &dummy, src, n); + ARCH->unlockMutex(s_mutex); + if (result < 0) + return -1; + else + return result; +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState) +{ + char dummy[MB_LEN_MAX]; + ARCH->lockMutex(s_mutex); + int n = wctomb(dst != NULL ? dst : dummy, src); + ARCH->unlockMutex(s_mutex); + return n; +} diff --git a/lib/arch/CMultibyteOS.cpp b/lib/arch/CMultibyteOS.cpp new file mode 100644 index 00000000..49a85229 --- /dev/null +++ b/lib/arch/CMultibyteOS.cpp @@ -0,0 +1,68 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +class CArchMBStateImpl { +public: + mbstate_t m_mbstate; +}; + +// +// use C library reentrant multibyte conversion +// + +static +void +initMB() +{ + // do nothing +} + +static +void +cleanMB() +{ + // do nothing +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ + return (mbsinit(&state->m_mbstate) != 0); +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, + int n, CArchMBState state) +{ + wchar_t dummy; + return static_cast(mbrtowc(dst != NULL ? dst : &dummy, + src, static_cast(n), &state->m_mbstate)); +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + char dummy[MB_LEN_MAX]; + return static_cast(wcrtomb(dst != NULL ? dst : dummy, + src, &state->m_mbstate)); +} diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h new file mode 100644 index 00000000..29c3f7bd --- /dev/null +++ b/lib/arch/IArchConsole.h @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHCONSOLE_H +#define IARCHCONSOLE_H + +#include "IInterface.h" + +class IArchConsole : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open the console + /*! + Opens the console for writing. The console is opened automatically + on the first write so calling this method is optional. Uses \c title + for the console's title if appropriate for the architecture. Calling + this method on an already open console must have no effect. + */ + virtual void openConsole(const char* title) = 0; + + //! Close the console + /*! + Close the console. Calling this method on an already closed console + must have no effect. + */ + virtual void closeConsole() = 0; + + //! Write to the console + /*! + Writes the given string to the console, opening it if necessary. + */ + virtual void writeConsole(const char*) = 0; + + //! Returns the newline sequence for the console + /*! + Different consoles use different character sequences for newlines. + This method returns the appropriate newline sequence for the console. + */ + virtual const char* getNewlineForConsole() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchDaemon.h b/lib/arch/IArchDaemon.h new file mode 100644 index 00000000..ce04eb43 --- /dev/null +++ b/lib/arch/IArchDaemon.h @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHDAEMON_H +#define IARCHDAEMON_H + +#include "IInterface.h" + +//! Interface for architecture dependent daemonizing +/*! +This interface defines the operations required by synergy for installing +uninstalling daeamons and daemonizing a process. Each architecture must +implement this interface. +*/ +class IArchDaemon : public IInterface { +public: + typedef int (*DaemonFunc)(int argc, const char** argv); + + //! @name manipulators + //@{ + + //! Install daemon + /*! + Install a daemon. \c name is the name of the daemon passed to the + system and \c description is a short human readable description of + the daemon. \c pathname is the path to the daemon executable. + \c commandLine should \b not include the name of program as the + first argument. If \c allUsers is true then the daemon will be + installed to start at boot time, otherwise it will be installed to + start when the current user logs in. Throws an \c XArchDaemon + exception on failure. + */ + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) = 0; + + //! Uninstall daemon + /*! + Uninstall a daemon. Throws an \c XArchDaemon on failure. + */ + virtual void uninstallDaemon(const char* name, bool allUsers) = 0; + + //! Daemonize the process + /*! + Daemonize. Throw XArchDaemonFailed on error. \c name is the name + of the daemon. Once daemonized, \c func is invoked and daemonize + returns when and what it does. + + Exactly what happens when daemonizing depends on the platform. +
    +
  • unix: + Detaches from terminal. \c func gets passed one argument, the + name passed to daemonize(). +
  • win32: + Becomes a service. Argument 0 is the name of the service + and the rest are the arguments passed to StartService(). + \c func is only called when the service is actually started. + \c func must call \c CArchMiscWindows::runDaemon() to finally + becoming a service. The \c runFunc function passed to \c runDaemon() + must call \c CArchMiscWindows::daemonRunning(true) when it + enters the main loop (i.e. after initialization) and + \c CArchMiscWindows::daemonRunning(false) when it leaves + the main loop. The \c stopFunc function passed to \c runDaemon() + is called when the daemon must exit the main loop and it must cause + \c runFunc to return. \c func should return what \c runDaemon() + returns. \c func or \c runFunc can call + \c CArchMiscWindows::daemonFailed() to indicate startup failure. +
+ */ + virtual int daemonize(const char* name, DaemonFunc func) = 0; + + //! Check if user has permission to install the daemon + /*! + Returns true iff the caller has permission to install or + uninstall the daemon. Note that even if this method returns + true it's possible that installing/uninstalling the service + may still fail. This method ignores whether or not the + service is already installed. + */ + virtual bool canInstallDaemon(const char* name, bool allUsers) = 0; + + //! Check if the daemon is installed + /*! + Returns true iff the daemon is installed. + */ + virtual bool isDaemonInstalled(const char* name, bool allUsers) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchFile.h b/lib/arch/IArchFile.h new file mode 100644 index 00000000..90b93649 --- /dev/null +++ b/lib/arch/IArchFile.h @@ -0,0 +1,59 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHFILE_H +#define IARCHFILE_H + +#include "IInterface.h" +#include "stdstring.h" + +class IArchFile : public IInterface { +public: + //! @name manipulators + //@{ + + //! Extract base name + /*! + Find the base name in the given \c pathname. + */ + virtual const char* getBasename(const char* pathname) = 0; + + //! Get user's home directory + /*! + Returns the user's home directory. Returns the empty string if + this cannot be determined. + */ + virtual std::string getUserDirectory() = 0; + + //! Get system directory + /*! + Returns the ussystem configuration file directory. + */ + virtual std::string getSystemDirectory() = 0; + + //! Concatenate path components + /*! + Concatenate pathname components with a directory separator + between them. This should not check if the resulting path + is longer than allowed by the system; we'll rely on the + system calls to tell us that. + */ + virtual std::string concatPath( + const std::string& prefix, + const std::string& suffix) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h new file mode 100644 index 00000000..dc818ebb --- /dev/null +++ b/lib/arch/IArchLog.h @@ -0,0 +1,49 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHLOG_H +#define IARCHLOG_H + +#include "IInterface.h" + +class IArchLog : public IInterface { +public: + enum ELevel { kERROR, kWARNING, kNOTE, kINFO, kDEBUG }; + + //! @name manipulators + //@{ + + //! Open the log + /*! + Opens the log for writing. The log must be opened before being + written to. + */ + virtual void openLog(const char* name) = 0; + + //! Close the log + /*! + Close the log. + */ + virtual void closeLog() = 0; + + //! Write to the log + /*! + Writes the given string to the log with the given level. + */ + virtual void writeLog(ELevel, const char*) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h new file mode 100644 index 00000000..a3b2efd2 --- /dev/null +++ b/lib/arch/IArchMultithread.h @@ -0,0 +1,80 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHMULTITHREAD_H +#define IARCHMULTITHREAD_H + +#include "IInterface.h" + +class CArchCondImpl; +class CArchMutexImpl; +class CArchThreadImpl; +typedef CArchCondImpl* CArchCond; +typedef CArchMutexImpl* CArchMutex; +typedef CArchThreadImpl* CArchThread; + +//! Interface for architecture dependent multithreading +/*! +This interface defines the multithreading operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchMultithread : public IInterface { +public: + typedef void* (*ThreadFunc)(void*); + typedef unsigned int ThreadID; + + //! @name manipulators + //@{ + + // + // condition variable methods + // + + virtual CArchCond newCondVar() = 0; + virtual void closeCondVar(CArchCond) = 0; + virtual void signalCondVar(CArchCond) = 0; + virtual void broadcastCondVar(CArchCond) = 0; + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout) = 0; + + // + // mutex methods + // + + virtual CArchMutex newMutex() = 0; + virtual void closeMutex(CArchMutex) = 0; + virtual void lockMutex(CArchMutex) = 0; + virtual void unlockMutex(CArchMutex) = 0; + + // + // thread methods + // + + virtual CArchThread newThread(ThreadFunc, void*) = 0; + virtual CArchThread newCurrentThread() = 0; + virtual CArchThread copyThread(CArchThread) = 0; + virtual void closeThread(CArchThread) = 0; + virtual void cancelThread(CArchThread) = 0; + virtual void setPriorityOfThread(CArchThread, int n) = 0; + virtual void testCancelThread() = 0; + virtual bool wait(CArchThread, double timeout) = 0; + virtual bool waitForEvent(double timeout) = 0; + virtual bool isSameThread(CArchThread, CArchThread) = 0; + virtual bool isExitedThread(CArchThread) = 0; + virtual void* getResultOfThread(CArchThread) = 0; + virtual ThreadID getIDOfThread(CArchThread) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h new file mode 100644 index 00000000..9bdacc71 --- /dev/null +++ b/lib/arch/IArchNetwork.h @@ -0,0 +1,105 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHNETWORK_H +#define IARCHNETWORK_H + +#include "IInterface.h" +#include "stdstring.h" + +class CArchSocketImpl; +class CArchNetAddressImpl; +typedef CArchSocketImpl* CArchSocket; +typedef CArchNetAddressImpl* CArchNetAddress; + +//! Interface for architecture dependent networking +/*! +This interface defines the networking operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchNetwork : public IInterface { +public: + enum EAddressFamily { + kUNKNOWN, + kINET, + }; + + enum ESocketType { + kDGRAM, + kSTREAM + }; + + enum { + kPOLLIN = 1, + kPOLLOUT = 2, + kPOLLERR = 4, + kPOLLNVAL = 8 + }; + + class CPollEntry { + public: + CArchSocket m_socket; + unsigned short m_events; + unsigned short m_revents; + }; + + //! @name manipulators + //@{ + + virtual CArchSocket newSocket(EAddressFamily, ESocketType) = 0; + virtual CArchSocket copySocket(CArchSocket s) = 0; + virtual void closeSocket(CArchSocket s) = 0; + virtual void closeSocketForRead(CArchSocket s) = 0; + virtual void closeSocketForWrite(CArchSocket s) = 0; + virtual void bindSocket(CArchSocket s, CArchNetAddress addr) = 0; + virtual void listenOnSocket(CArchSocket s) = 0; + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr) = 0; + virtual void connectSocket(CArchSocket s, CArchNetAddress name) = 0; + virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; + virtual size_t readSocket(CArchSocket s, void* buf, size_t len) = 0; + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len) = 0; + virtual void throwErrorOnSocket(CArchSocket) = 0; + + //! Set socket to (non-)blocking operation + /*! + Set socket to block or not block on accept, connect, poll, read and + write (i.e. calls that may take an arbitrary amount of time). + Returns the previous state. + */ + virtual bool setBlockingOnSocket(CArchSocket, bool blocking) = 0; + + //! Turn Nagle algorithm on or off on socket + /*! + Set socket to send messages immediately (true) or to collect small + messages into one packet (false). Returns the previous state. + */ + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0; + + virtual std::string getHostName() = 0; + virtual CArchNetAddress newAnyAddr(EAddressFamily) = 0; + virtual CArchNetAddress copyAddr(CArchNetAddress) = 0; + virtual CArchNetAddress nameToAddr(const std::string&) = 0; + virtual void closeAddr(CArchNetAddress) = 0; + virtual std::string addrToName(CArchNetAddress) = 0; + virtual std::string addrToString(CArchNetAddress) = 0; + virtual EAddressFamily getAddrFamily(CArchNetAddress) = 0; + virtual void setAddrPort(CArchNetAddress, int port) = 0; + virtual int getAddrPort(CArchNetAddress) = 0; + virtual bool isAnyAddr(CArchNetAddress) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchSleep.h b/lib/arch/IArchSleep.h new file mode 100644 index 00000000..df6e88e8 --- /dev/null +++ b/lib/arch/IArchSleep.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSLEEP_H +#define IARCHSLEEP_H + +#include "IInterface.h" + +class IArchSleep : public IInterface { +public: + //! @name manipulators + //@{ + + //! Sleep + /*! + Blocks the calling thread for \c timeout seconds. If + \c timeout < 0.0 then the call returns immediately. If \c timeout + == 0.0 then the calling thread yields the CPU. + + (cancellation point) + */ + virtual void sleep(double timeout) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchString.h b/lib/arch/IArchString.h new file mode 100644 index 00000000..d8117c30 --- /dev/null +++ b/lib/arch/IArchString.h @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSTRING_H +#define IARCHSTRING_H + +#include "IInterface.h" +#include + +class CArchMBStateImpl; +typedef CArchMBStateImpl* CArchMBState; + +class IArchString : public IInterface { +public: + //! @name manipulators + //@{ + + //! printf() to limited size buffer with va_list + /*! + This method is equivalent to vsprintf() except it will not write + more than \c n bytes to the buffer, returning -1 if the output + was truncated and the number of bytes written not including the + trailing NUL otherwise. + */ + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap) = 0; + + //! Create a new multibyte conversion state + virtual CArchMBState newMBState() = 0; + + //! Destroy a multibyte conversion state + virtual void closeMBState(CArchMBState) = 0; + + //! Initialize a multibyte conversion state + virtual void initMBState(CArchMBState) = 0; + + //! Test a multibyte conversion state + virtual bool isInitMBState(CArchMBState) = 0; + + //! Convert multibyte to wide character + virtual int convMBToWC(wchar_t*, + const char*, int, CArchMBState) = 0; + + //! Convert wide character to multibyte + virtual int convWCToMB(char*, wchar_t, CArchMBState) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTime.h b/lib/arch/IArchTime.h new file mode 100644 index 00000000..b2b9a5cd --- /dev/null +++ b/lib/arch/IArchTime.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTIME_H +#define IARCHTIME_H + +#include "IInterface.h" + +class IArchTime : public IInterface { +public: + //! @name manipulators + //@{ + + //! Get the current time + /*! + Returns the number of seconds since some arbitrary starting time. + This should return as high a precision as reasonable. + */ + virtual double time() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am new file mode 100644 index 00000000..e495adf8 --- /dev/null +++ b/lib/arch/Makefile.am @@ -0,0 +1,96 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + arch.dsp \ + CArchConsoleWindows.cpp \ + CArchDaemonWindows.cpp \ + CArchFileWindows.cpp \ + CArchLogWindows.cpp \ + CArchMiscWindows.cpp \ + CArchMultithreadWindows.cpp \ + CArchNetworkWinsock.cpp \ + CArchSleepWindows.cpp \ + CArchStringWindows.cpp \ + CArchTimeWindows.cpp \ + XArchWindows.cpp \ + CArchConsoleWindows.h \ + CArchDaemonWindows.h \ + CArchFileWindows.h \ + CArchLogWindows.h \ + CArchMiscWindows.h \ + CArchMultithreadWindows.h \ + CArchNetworkWinsock.h \ + CArchSleepWindows.h \ + CArchStringWindows.h \ + CArchTimeWindows.h \ + XArchWindows.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libarch.a +libarch_a_SOURCES = \ + CArch.cpp \ + CArchImpl.cpp \ + XArch.cpp \ + CArch.h \ + IArchConsole.h \ + IArchDaemon.h \ + IArchFile.h \ + IArchLog.h \ + IArchMultithread.h \ + IArchNetwork.h \ + IArchSleep.h \ + IArchString.h \ + IArchTime.h \ + XArch.h \ + XArchImpl.h \ + $(NULL) +EXTRA_libarch_a_SOURCES = \ + CArchConsoleUnix.cpp \ + CArchDaemonNone.cpp \ + CArchDaemonUnix.cpp \ + CArchFileUnix.cpp \ + CArchLogUnix.cpp \ + CArchMultithreadPosix.cpp \ + CArchNetworkBSD.cpp \ + CArchSleepUnix.cpp \ + CArchStringUnix.cpp \ + CArchTimeUnix.cpp \ + CMultibyte.cpp \ + CMultibyteOS.cpp \ + CMultibyteEmu.cpp \ + XArchUnix.cpp \ + vsnprintf.cpp \ + CArchConsoleUnix.h \ + CArchDaemonNone.h \ + CArchDaemonUnix.h \ + CArchFileUnix.h \ + CArchLogUnix.h \ + CArchMultithreadPosix.h \ + CArchNetworkBSD.h \ + CArchSleepUnix.h \ + CArchStringUnix.h \ + CArchTimeUnix.h \ + XArchUnix.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + $(NULL) diff --git a/lib/arch/XArch.cpp b/lib/arch/XArch.cpp new file mode 100644 index 00000000..9dce5283 --- /dev/null +++ b/lib/arch/XArch.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArch.h" + +// +// XArch +// + +std::string +XArch::what() const throw() +{ + try { + if (m_what.empty() && m_eval != NULL) { + m_what = m_eval->eval(); + } + } + catch (...) { + // ignore + } + return m_what; +} diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h new file mode 100644 index 00000000..131e07ed --- /dev/null +++ b/lib/arch/XArch.h @@ -0,0 +1,165 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCH_H +#define XARCH_H + +#include "stdstring.h" + +//! Generic thread exception +/*! +Exceptions derived from this class are used by the multithreading +library to perform stack unwinding when a thread terminates. These +exceptions must always be rethrown by clients when caught. +*/ +class XThread { }; + +//! Thread exception to cancel +/*! +Thrown to cancel a thread. Clients must not throw this type, but +must rethrow it if caught (by XThreadCancel, XThread, or ...). +*/ +class XThreadCancel : public XThread { }; + +/*! +\def RETHROW_XTHREAD +Convenience macro to rethrow an XThread exception but ignore other +exceptions. Put this in your catch (...) handler after necessary +cleanup but before leaving or returning from the handler. +*/ +#define RETHROW_XTHREAD \ + try { throw; } catch (XThread&) { throw; } catch (...) { } + +//! Lazy error message string evaluation +/*! +This class encapsulates platform dependent error string lookup. +Platforms subclass this type, taking an appropriate error code +type in the c'tor and overriding eval() to return the error +string for that error code. +*/ +class XArchEval { +public: + XArchEval() { } + virtual ~XArchEval() { } + + virtual XArchEval* clone() const throw() = 0; + + virtual std::string eval() const throw() = 0; +}; + +//! Generic exception architecture dependent library +class XArch { +public: + XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { } + XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { } + XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { } + ~XArch() { delete m_eval; } + + std::string what() const throw(); + +private: + XArchEval* m_eval; + mutable std::string m_what; +}; + +// Macro to declare XArch derived types +#define XARCH_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_(XArchEval* adoptedEvaluator) : super_(adoptedEvaluator) { } \ + name_(const std::string& msg) : super_(msg) { } \ +} + +//! Generic network exception +/*! +Exceptions derived from this class are used by the networking +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchNetwork, XArch); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkWouldBlock, XArchNetwork); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork); + +//! Network insufficient resources +XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork); + +//! No support for requested network resource/service +XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork); + +//! Network I/O error +XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork); + +//! Network address is unavailable or not local +XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork); + +//! Network address in use +XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork); + +//! No route to address +XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork); + +//! Socket not connected +XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork); + +//! Remote end of socket has disconnected +XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork); + +//! Remote end of socket refused connection +XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork); + +//! Connection is in progress +XARCH_SUBCLASS(XArchNetworkConnecting, XArchNetwork); + +//! Remote end of socket is not responding +XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork); + +//! Generic network name lookup erros +XARCH_SUBCLASS(XArchNetworkName, XArchNetwork); + +//! The named host is unknown +XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName); + +//! The named host is known but has to address +XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName); + +//! Non-recoverable name server error +XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName); + +//! Temporary name server error +XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName); + +//! Generic daemon exception +/*! +Exceptions derived from this class are used by the daemon +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchDaemon, XArch); + +//! Could not daemonize +XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon); + +//! Could not install daemon +XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon); + +//! Could not uninstall daemon +XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon); + +//! Attempted to uninstall a daemon that was not installed +XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed); + + +#endif diff --git a/lib/platform/CPlatform.cpp b/lib/arch/XArchImpl.h similarity index 79% rename from lib/platform/CPlatform.cpp rename to lib/arch/XArchImpl.h index 091cd650..28c79721 100644 --- a/lib/platform/CPlatform.cpp +++ b/lib/arch/XArchImpl.h @@ -12,18 +12,14 @@ * GNU General Public License for more details. */ -#include "common.h" +#ifndef XARCHIMPL_H +#define XARCHIMPL_H +// include appropriate architecture implementation #if WINDOWS_LIKE - -#include "CWin32Platform.cpp" - +# include "XArchWindows.h" #elif UNIX_LIKE - -#include "CUnixPlatform.cpp" - -#else - -#error Unsupported platform +# include "XArchUnix.h" +#endif #endif diff --git a/lib/arch/XArchUnix.cpp b/lib/arch/XArchUnix.cpp new file mode 100644 index 00000000..6f4047d5 --- /dev/null +++ b/lib/arch/XArchUnix.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchUnix.h" +#include + +// +// XArchEvalUnix +// + +XArchEval* +XArchEvalUnix::clone() const throw() +{ + return new XArchEvalUnix(m_errno); +} + +std::string +XArchEvalUnix::eval() const throw() +{ + // FIXME -- not thread safe + return strerror(m_errno); +} diff --git a/lib/arch/XArchUnix.h b/lib/arch/XArchUnix.h new file mode 100644 index 00000000..19e0df4c --- /dev/null +++ b/lib/arch/XArchUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHUNIX_H +#define XARCHUNIX_H + +#include "XArch.h" + +//! Lazy error message string evaluation for unix +class XArchEvalUnix : public XArchEval { +public: + XArchEvalUnix(int err) : m_errno(err) { } + virtual ~XArchEvalUnix() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/XArchWindows.cpp b/lib/arch/XArchWindows.cpp new file mode 100644 index 00000000..a57bc71c --- /dev/null +++ b/lib/arch/XArchWindows.cpp @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchWindows.h" + +// +// XArchEvalWindows +// + +XArchEval* +XArchEvalWindows::clone() const throw() +{ + return new XArchEvalWindows(m_errno); +} + +std::string +XArchEvalWindows::eval() const throw() +{ + char* cmsg; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + m_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&cmsg, + 0, + NULL) == 0) { + cmsg = NULL; + return "Unknown error"; + } + std::string smsg(cmsg); + LocalFree(cmsg); + return smsg; +} + + +// +// XArchEvalWinsock +// + +XArchEval* +XArchEvalWinsock::clone() const throw() +{ + return new XArchEvalWinsock(m_errno); +} + +std::string +XArchEvalWinsock::eval() const throw() +{ + // built-in windows function for looking up error message strings + // may not look up network error messages correctly. we'll have + // to do it ourself. + static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { + /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, + /* 10009 */{WSAEBADF, "Bad file handle"}, + /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, + /* 10014 */{WSAEFAULT, "WSAEFAULT"}, + /* 10022 */{WSAEINVAL, "WSAEINVAL"}, + /* 10024 */{WSAEMFILE, "No more file descriptors available"}, + /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, + /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, + /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, + /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, + /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, + /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, + /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, + /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, + /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, + /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, + /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, + /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, + /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, + /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, + /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, + /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, + /* 10051 */{WSAENETUNREACH, "The network can't be reached from this hos at this time"}, + /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, + /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, + /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, + /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, + /* 10056 */{WSAEISCONN, "The socket is already connected"}, + /* 10057 */{WSAENOTCONN, "The socket is not connected"}, + /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, + /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, + /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, + /* 10061 */{WSAECONNREFUSED, "The attempt to connect was forcefully rejected"}, + /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, + /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, + /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, + /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, + /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, + /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, + /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, + /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, + /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, + /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, + /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, + /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, + /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, + /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, + /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, + /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, + /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, + /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, + /* end */{0, NULL} + }; + + for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { + if (s_netErrorCodes[i].m_code == m_errno) { + return s_netErrorCodes[i].m_msg; + } + } + return "Unknown error"; +} diff --git a/lib/arch/XArchWindows.h b/lib/arch/XArchWindows.h new file mode 100644 index 00000000..56c24007 --- /dev/null +++ b/lib/arch/XArchWindows.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHWINDOWS_H +#define XARCHWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "XArch.h" +#include + +//! Lazy error message string evaluation for windows +class XArchEvalWindows : public XArchEval { +public: + XArchEvalWindows() : m_errno(GetLastError()) { } + XArchEvalWindows(DWORD err) : m_errno(err) { } + virtual ~XArchEvalWindows() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + DWORD m_errno; +}; + +//! Lazy error message string evaluation for winsock +class XArchEvalWinsock : public XArchEval { +public: + XArchEvalWinsock(int err) : m_errno(err) { } + virtual ~XArchEvalWinsock() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp new file mode 100644 index 00000000..1af4c4b7 --- /dev/null +++ b/lib/arch/arch.dsp @@ -0,0 +1,269 @@ +# Microsoft Developer Studio Project File - Name="arch" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=arch - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "arch.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "arch.mak" CFG="arch - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "arch - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "arch - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "arch - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "arch - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "arch - Win32 Release" +# Name "arch - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CArch.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.cpp +# End Source File +# Begin Source File + +SOURCE=.\XArch.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CArch.h +# End Source File +# Begin Source File + +SOURCE=.\CArchConsoleWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.h +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.h +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.h +# End Source File +# Begin Source File + +SOURCE=.\IArchConsole.h +# End Source File +# Begin Source File + +SOURCE=.\IArchLog.h +# End Source File +# Begin Source File + +SOURCE=.\IArchMultithread.h +# End Source File +# Begin Source File + +SOURCE=.\IArchNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\IArchSleep.h +# End Source File +# Begin Source File + +SOURCE=.\IArchString.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTime.h +# End Source File +# Begin Source File + +SOURCE=.\XArch.h +# End Source File +# Begin Source File + +SOURCE=.\XArchImpl.h +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.h +# End Source File +# End Group +# Begin Group "Included Files" + +# PROP Default_Filter "inc" +# Begin Source File + +SOURCE=.\CArchConsoleWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchFileWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyte.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteEmu.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteOS.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\vsnprintf.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp new file mode 100644 index 00000000..4bd665d2 --- /dev/null +++ b/lib/arch/vsnprintf.cpp @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if HAVE_VSNPRINTF + +#if !defined(ARCH_VSNPRINTF) +# define ARCH_VSNPRINTF vsnprintf +#endif + +int +ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + int n = ::ARCH_VSNPRINTF(str, size, fmt, ap); + if (n > size) { + n = -1; + } + return n; +} + +#else // !HAVE_VSNPRINTF + +// FIXME +#error vsnprintf not implemented + +#endif // !HAVE_VSNPRINTF diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 0cc48f08..84fe9994 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -14,15 +14,13 @@ #include "CLog.h" #include "CString.h" +#include "CStringUtil.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "Version.h" #include -#include #include -#if WINDOWS_LIKE -#define WIN32_LEAN_AND_MEAN -#include -#endif - // names of priorities static const char* g_priority[] = { "FATAL", @@ -56,26 +54,64 @@ static const int g_prioritySuffixLength = 2; static const int g_priorityPad = g_maxPriorityLength + g_prioritySuffixLength; -// platform newline sequence -#if WINDOWS_LIKE -static const char* g_newline = "\r\n"; -#else -static const char* g_newline = "\n"; -#endif +// +// CLogLock +// +// Convenience object to lock/unlock a mutex. +// + +class CLogLock { +public: + CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); } + ~CLogLock() { ARCH->unlockMutex(m_mutex); } + +private: + CArchMutex m_mutex; +}; -// minimum length of a newline sequence -static const int g_newlineLength = 2; // // CLog // -CLog::Outputter CLog::s_outputter = NULL; -CLog::Lock CLog::s_lock = &CLog::dummyLock; -int CLog::s_maxPriority = -1; +CLog* CLog::s_log = NULL; + +CLog::CLog() +{ + assert(s_log == NULL); + + // create mutex for multithread safe operation + m_mutex = ARCH->newMutex(); + + // other initalization + m_maxPriority = g_defaultMaxPriority; + m_maxNewlineLength = 0; + insert(new CConsoleLogOutputter); +} + +CLog::~CLog() +{ + // clean up + for (COutputterList::iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + delete *index; + } + ARCH->closeMutex(m_mutex); + s_log = NULL; +} + +CLog* +CLog::getInstance() +{ + // note -- not thread safe; client must initialize log safely + if (s_log == NULL) { + s_log = new CLog; + } + return s_log; +} void -CLog::print(const char* fmt, ...) +CLog::print(const char* fmt, ...) const { // check if fmt begins with a priority argument int priority = 4; @@ -85,7 +121,7 @@ CLog::print(const char* fmt, ...) } // done if below priority threshold - if (priority > getMaxPriority()) { + if (priority > getFilter()) { return; } @@ -98,7 +134,7 @@ CLog::print(const char* fmt, ...) va_start(args, fmt); char* buffer = CStringUtil::vsprint(stack, sizeof(stack) / sizeof(stack[0]), - pad, g_newlineLength, fmt, args); + pad, m_maxNewlineLength, fmt, args); va_end(args); // output buffer @@ -110,7 +146,7 @@ CLog::print(const char* fmt, ...) } void -CLog::printt(const char* file, int line, const char* fmt, ...) +CLog::printt(const char* file, int line, const char* fmt, ...) const { // check if fmt begins with a priority argument int priority = 4; @@ -120,7 +156,7 @@ CLog::printt(const char* file, int line, const char* fmt, ...) } // done if below priority threshold - if (priority > getMaxPriority()) { + if (priority > getFilter()) { return; } @@ -136,7 +172,7 @@ CLog::printt(const char* file, int line, const char* fmt, ...) va_start(args, fmt); char* buffer = CStringUtil::vsprint(stack, sizeof(stack) / sizeof(stack[0]), - pad, g_newlineLength, fmt, args); + pad, m_maxNewlineLength, fmt, args); va_end(args); // print the prefix to the buffer. leave space for priority label. @@ -158,31 +194,34 @@ CLog::printt(const char* file, int line, const char* fmt, ...) } void -CLog::setOutputter(Outputter outputter) +CLog::insert(ILogOutputter* outputter) { - CHoldLock lock(s_lock); - s_outputter = outputter; -} + assert(outputter != NULL); + assert(outputter->getNewline() != NULL); -CLog::Outputter -CLog::getOutputter() -{ - CHoldLock lock(s_lock); - return s_outputter; + CLogLock lock(m_mutex); + m_outputters.push_front(outputter); + int newlineLength = strlen(outputter->getNewline()); + if (newlineLength > m_maxNewlineLength) { + m_maxNewlineLength = newlineLength; + } } void -CLog::setLock(Lock newLock) +CLog::remove(ILogOutputter* outputter) { - CHoldLock lock(s_lock); - s_lock = (newLock == NULL) ? dummyLock : newLock; + CLogLock lock(m_mutex); + m_outputters.remove(outputter); } -CLog::Lock -CLog::getLock() +void +CLog::pop_front() { - CHoldLock lock(s_lock); - return (s_lock == dummyLock) ? NULL : s_lock; + CLogLock lock(m_mutex); + if (!m_outputters.empty()) { + delete m_outputters.front(); + m_outputters.pop_front(); + } } bool @@ -203,38 +242,19 @@ CLog::setFilter(const char* maxPriority) void CLog::setFilter(int maxPriority) { - CHoldLock lock(s_lock); - s_maxPriority = maxPriority; + CLogLock lock(m_mutex); + m_maxPriority = maxPriority; } int -CLog::getFilter() +CLog::getFilter() const { - CHoldLock lock(s_lock); - return getMaxPriority(); + CLogLock lock(m_mutex); + return m_maxPriority; } void -CLog::dummyLock(bool) -{ - // do nothing -} - -int -CLog::getMaxPriority() -{ - CHoldLock lock(s_lock); - - if (s_maxPriority == -1) { - s_maxPriority = g_defaultMaxPriority; - setFilter(getenv("SYN_LOG_PRI")); - } - - return s_maxPriority; -} - -void -CLog::output(int priority, char* msg) +CLog::output(int priority, char* msg) const { assert(priority >= -1 && priority < g_numPriority); assert(msg != NULL); @@ -249,79 +269,23 @@ CLog::output(int priority, char* msg) msg[g_maxPriorityLength + 1] = ' '; } - // put a newline at the end - strcat(msg + g_priorityPad, g_newline); + // write to each outputter + CLogLock lock(m_mutex); + for (COutputterList::const_iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); - // print it - CHoldLock lock(s_lock); - if (s_outputter == NULL || - !s_outputter(priority, msg + g_maxPriorityLength - n)) { -#if WINDOWS_LIKE - openConsole(); -#endif - fprintf(stderr, "%s", msg + g_maxPriorityLength - n); + // open the outputter + outputter->open(kApplication); + + // write message and break out of loop if it returns false + if (!outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n)) { + break; + } } } - -#if WINDOWS_LIKE - -static DWORD s_thread = 0; - -static -BOOL WINAPI -CLogSignalHandler(DWORD) -{ - // terminate cleanly and skip remaining handlers - PostThreadMessage(s_thread, WM_QUIT, 0, 0); - return TRUE; -} - -void -CLog::openConsole() -{ - static bool s_hasConsole = false; - - // ignore if already created - if (s_hasConsole) - return; - - // remember the current thread. when we get a ctrl+break or the - // console is closed we'll post WM_QUIT to this thread to shutdown - // cleanly. - // note -- win95/98/me are broken and will not receive a signal - // when the console is closed nor during logoff or shutdown, - // see microsoft articles Q130717 and Q134284. we could work - // around this in a painful way using hooks and hidden windows - // (as apache does) but it's not worth it. the app will still - // quit, just not cleanly. users in-the-know can use ctrl+c. - s_thread = GetCurrentThreadId(); - - // open a console - if (!AllocConsole()) - return; - - // get the handle for error output - HANDLE herr = GetStdHandle(STD_ERROR_HANDLE); - - // prep console. windows 95 and its ilk have braindead - // consoles that can't even resize independently of the - // buffer size. use a 25 line buffer for those systems. - OSVERSIONINFO osInfo; - COORD size = { 80, 1000 }; - osInfo.dwOSVersionInfoSize = sizeof(osInfo); - if (GetVersionEx(&osInfo) && - osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) - size.Y = 25; - SetConsoleScreenBufferSize(herr, size); - SetConsoleTextAttribute(herr, - FOREGROUND_RED | - FOREGROUND_GREEN | - FOREGROUND_BLUE); - SetConsoleCtrlHandler(CLogSignalHandler, TRUE); - - // reopen stderr to point at console - freopen("con", "w", stderr); - s_hasConsole = true; -} - -#endif diff --git a/lib/base/CLog.h b/lib/base/CLog.h index 302e748a..d76f9140 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -16,8 +16,15 @@ #define CLOG_H #include "common.h" +#include "IArchMultithread.h" +#include "IInterface.h" +#include "stdlist.h" #include +#define CLOG (CLog::getInstance()) + +class ILogOutputter; + //! Logging facility /*! The logging class; all console output should go through this class. @@ -42,56 +49,51 @@ public: kDEBUG2 //!< For even more detailed debugging messages }; - //! Outputter function. - /*! - Type of outputter function. The outputter should write \c message, - which has the given \c priority, to a log and return true. Or it can - return false to let CLog use the default outputter. - */ - typedef bool (*Outputter)(int priority, const char* message); - - //! Locking function - /*! - Type of lock/unlock function. If \c lock is true then block other - threads that try to lock until this thread unlocks. If \c lock is - false then unlock and allow another (waiting) thread to lock. - */ - typedef void (*Lock)(bool lock); + ~CLog(); //! @name manipulators //@{ - //! Set the function used to write the log + //! Add an outputter to the head of the list /*! - Sets the function used to write to the log. The outputter function - is called with the formatted string to write and the priority level. - CLog will have already filtered messages below the current filter - priority. A NULL outputter means to use the default which is to print - to stderr. Note that the outputter should not call CLog methods but, - if it does, the current lock function must permit recursive locks. - */ - static void setOutputter(Outputter); + Inserts an outputter to the head of the outputter list. When the + logger writes a message, it goes to the outputter at the head of + the outputter list. If that outputter's \c write() method returns + true then it also goes to the next outputter, as so on until an + outputter returns false or there are no more outputters. Outputters + still in the outputter list when the log is destroyed will be + deleted. - //! Set the lock/unlock function - /*! - Set the lock/unlock function. Use setLock(NULL) to remove the - locking function. There is no default lock function; do not call - CLog from multiple threads unless a working lock function has been - installed. + By default, the logger has one outputter installed which writes to + the console. */ - static void setLock(Lock); + void insert(ILogOutputter* adopted); + + //! Remove an outputter from the list + /*! + Removes the first occurrence of the given outputter from the + outputter list. It does nothing if the outputter is not in the + list. The outputter is not deleted. + */ + void remove(ILogOutputter* orphaned); + + //! Remove the outputter from the head of the list + /*! + Removes and deletes the outputter at the head of the outputter list. + This does nothing if the outputter list is empty. + */ + void pop_front(); //! Set the minimum priority filter. /*! Set the filter. Messages below this priority are discarded. The default priority is 4 (INFO) (unless built without NDEBUG - in which case it's 5 (DEBUG)). The default can be overridden - by setting the SYN_LOG_PRI env var to "FATAL", "ERROR", etc. - setFilter(const char*) returns true if the priority \c name was - recognized; if \c name is NULL then it simply returns true. + in which case it's 5 (DEBUG)). setFilter(const char*) returns + true if the priority \c name was recognized; if \c name is NULL + then it simply returns true. */ - static bool setFilter(const char* name); - static void setFilter(int); + bool setFilter(const char* name); + void setFilter(int); //@} //! @name accessors @@ -101,52 +103,38 @@ public: /*! Print a log message using the printf-like \c format and arguments. */ - static void print(const char* format, ...); + void print(const char* format, ...) const; //! Print a log message /*! Print a log message using the printf-like \c format and arguments preceded by the filename and line number. */ - static void printt(const char* file, int line, - const char* format, ...); - - //! Get the function used to write the log - static Outputter getOutputter(); - - //! Get the lock/unlock function - /*! - Get the lock/unlock function. Note that the lock function is - used when retrieving the lock function. - */ - static Lock getLock(); + void printt(const char* file, int line, + const char* format, ...) const; //! Get the minimum priority level. - static int getFilter(); + int getFilter() const; + + //! Get the singleton instance of the log + static CLog* getInstance(); //@} private: - class CHoldLock { - public: - CHoldLock(Lock lock) : m_lock(lock) { m_lock(true); } - ~CHoldLock() { m_lock(false); } + CLog(); - private: - Lock m_lock; - }; - - static void dummyLock(bool); - static int getMaxPriority(); - static void output(int priority, char* msg); -#if WINDOWS_LIKE - static void openConsole(); -#endif + void output(int priority, char* msg) const; private: - static Outputter s_outputter; - static Lock s_lock; - static int s_maxPriority; + typedef std::list COutputterList; + + static CLog* s_log; + + CArchMutex m_mutex; + COutputterList m_outputters; + int m_maxNewlineLength; + int m_maxPriority; }; /*! @@ -193,12 +181,12 @@ which includes the filename and line number. #define LOGC(_a1, _a2) #define CLOG_TRACE #elif defined(NDEBUG) -#define LOG(_a1) CLog::print _a1 -#define LOGC(_a1, _a2) if (_a1) CLog::print _a2 +#define LOG(_a1) CLOG->print _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 #define CLOG_TRACE #else -#define LOG(_a1) CLog::printt _a1 -#define LOGC(_a1, _a2) if (_a1) CLog::printt _a2 +#define LOG(_a1) CLOG->printt _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->printt _a2 #define CLOG_TRACE __FILE__, __LINE__, #endif diff --git a/lib/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp index 02b99d77..89edce2b 100644 --- a/lib/base/CStopwatch.cpp +++ b/lib/base/CStopwatch.cpp @@ -13,6 +13,7 @@ */ #include "CStopwatch.h" +#include "CArch.h" // // CStopwatch @@ -24,7 +25,7 @@ CStopwatch::CStopwatch(bool triggered) : m_stopped(triggered) { if (!triggered) { - m_mark = getClock(); + m_mark = ARCH->time(); } } @@ -42,7 +43,7 @@ CStopwatch::reset() return dt; } else { - const double t = getClock(); + const double t = ARCH->time(); const double dt = t - m_mark; m_mark = t; return dt; @@ -57,7 +58,7 @@ CStopwatch::stop() } // save the elapsed time - m_mark = getClock() - m_mark; + m_mark = ARCH->time() - m_mark; m_stopped = true; } @@ -70,7 +71,7 @@ CStopwatch::start() } // set the mark such that it reports the time elapsed at stop() - m_mark = getClock() - m_mark; + m_mark = ARCH->time() - m_mark; m_stopped = false; } @@ -93,7 +94,7 @@ CStopwatch::getTime() return m_mark; } else { - return getClock() - m_mark; + return ARCH->time() - m_mark; } } @@ -115,7 +116,7 @@ CStopwatch::getTime() const return m_mark; } else { - return getClock() - m_mark; + return ARCH->time() - m_mark; } } @@ -123,100 +124,3 @@ CStopwatch::operator double() const { return getTime(); } - -#if WINDOWS_LIKE - -// avoid getting a lot a crap from mmsystem.h that we don't need -#define MMNODRV // Installable driver support -#define MMNOSOUND // Sound support -#define MMNOWAVE // Waveform support -#define MMNOMIDI // MIDI support -#define MMNOAUX // Auxiliary audio support -#define MMNOMIXER // Mixer support -#define MMNOJOY // Joystick support -#define MMNOMCI // MCI support -#define MMNOMMIO // Multimedia file I/O support -#define MMNOMMSYSTEM // General MMSYSTEM functions - -#define WIN32_LEAN_AND_MEAN -#include -#include - -typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void); - -static double s_freq = 0.0; -static HINSTANCE s_mmInstance = NULL; -static PTimeGetTime s_tgt = NULL; - -// -// initialize local variables -// - -class CStopwatchInit { -public: - CStopwatchInit(); - ~CStopwatchInit(); -}; -static CStopwatchInit s_init; - -CStopwatchInit::CStopwatchInit() -{ - LARGE_INTEGER freq; - if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) { - s_freq = 1.0 / static_cast(freq.QuadPart); - } - else { - // load winmm.dll and get timeGetTime - s_mmInstance = LoadLibrary("winmm"); - if (s_mmInstance) { - s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); - } - } -} - -CStopwatchInit::~CStopwatchInit() -{ - if (s_mmInstance) { - FreeLibrary(reinterpret_cast(s_mmInstance)); - } -} - -double -CStopwatch::getClock() const -{ - // get time. we try three ways, in order of descending precision - if (s_freq != 0.0) { - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - return s_freq * static_cast(c.QuadPart); - } - else if (s_tgt) { - return 0.001 * static_cast(s_tgt()); - } - else { - return 0.001 * static_cast(GetTickCount()); - } -} - -#elif UNIX_LIKE - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -double -CStopwatch::getClock() const -{ - struct timeval t; - gettimeofday(&t, NULL); - return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; -} - -#endif // UNIX_LIKE diff --git a/lib/base/CString.h b/lib/base/CString.h index 7add626f..e3eaa3b0 100644 --- a/lib/base/CString.h +++ b/lib/base/CString.h @@ -15,90 +15,10 @@ #ifndef CSTRING_H #define CSTRING_H -#include -#include "stdpre.h" -#include -#include "stdpost.h" +#include "stdstring.h" // use standard C++ string class for our string class typedef std::string CString; -//! String utilities -/*! -This class provides various functions for string manipulation. -*/ -class CStringUtil { -public: - //! Format positional arguments - /*! - Format a string using positional arguments. fmt has literal - characters and conversion specifications introduced by `\%': - - \c\%\% -- literal `\%' - - \c\%{n} -- positional element n, n a positive integer, {} are literal - - All arguments in the variable list are const char*. Positional - elements are indexed from 1. - */ - static CString format(const char* fmt, ...); - - //! Format positional arguments - /*! - Same as format() except takes va_list. - */ - static CString vformat(const char* fmt, va_list); - - //! Print a string using printf-style formatting - /*! - Equivalent to printf() except the result is returned as a CString. - */ - static CString print(const char* fmt, ...); - - //! Print a string using printf-style formatting - /*! - Same as print() except takes va_list. - */ - static CString vprint(const char* fmt, va_list); - - //! Print a string using printf-style formatting into a buffer - /*! - This is like print but print into a given buffer. If the resulting - string will not fit into \c buffer then a new buffer is allocated and - returned, otherwise \c buffer is returned. the caller must delete[] - the returned memory if is not \c buffer. - - \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and - at least \c suffix characters are available in the buffer before - and after the printed string, respectively. \c bufferLength is the - length of buffer and should not be adjusted by the caller to - account for \c prefix or \c suffix. - */ - static char* vsprint(char* buffer, int bufferLength, - int prefix, int suffix, const char* fmt, va_list); - - //! Case-insensitive comparisons - /*! - This class provides case-insensitve comparison functions. - */ - class CaselessCmp { - public: - //! Same as less() - bool operator()(const CString& a, const CString& b) const; - - //! Returns true iff \c a is lexicographically less than \c b - static bool less(const CString& a, const CString& b); - - //! Returns true iff \c a is lexicographically equal to \c b - static bool equal(const CString& a, const CString& b); - - //! Returns true iff \c a is lexicographically less than \c b - static bool cmpLess(const CString::value_type& a, - const CString::value_type& b); - - //! Returns true iff \c a is lexicographically equal to \c b - static bool cmpEqual(const CString::value_type& a, - const CString::value_type& b); - }; -}; - #endif diff --git a/lib/base/CString.cpp b/lib/base/CStringUtil.cpp similarity index 94% rename from lib/base/CString.cpp rename to lib/base/CStringUtil.cpp index 13e78239..2e61c047 100644 --- a/lib/base/CString.cpp +++ b/lib/base/CStringUtil.cpp @@ -12,7 +12,8 @@ * GNU General Public License for more details. */ -#include "CString.h" +#include "CStringUtil.h" +#include "CArch.h" #include "common.h" #include "stdvector.h" #include @@ -20,12 +21,6 @@ #include #include -#if WINDOWS_LIKE -#define WIN32_LEAN_AND_MEAN -#include -#define vsnprintf _vsnprintf -#endif - // // CStringUtil // @@ -152,7 +147,8 @@ CStringUtil::vsprint(char* buffer, int len, // try writing to input buffer int n; if (buffer != NULL && len >= prefix + suffix) { - n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); if (n >= 0 && n <= len - (prefix + suffix)) return buffer; } @@ -163,7 +159,8 @@ CStringUtil::vsprint(char* buffer, int len, delete[] buffer; len *= 2; buffer = new char[len + (prefix + suffix)]; - n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); } while (n < 0 || n > len - (prefix + suffix)); return buffer; diff --git a/lib/base/CStringUtil.h b/lib/base/CStringUtil.h new file mode 100644 index 00000000..1ad5824c --- /dev/null +++ b/lib/base/CStringUtil.h @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTRINGUTIL_H +#define CSTRINGUTIL_H + +#include "CString.h" +#include + +//! String utilities +/*! +This class provides various functions for string manipulation. +*/ +class CStringUtil { +public: + //! Format positional arguments + /*! + Format a string using positional arguments. fmt has literal + characters and conversion specifications introduced by `\%': + - \c\%\% -- literal `\%' + - \c\%{n} -- positional element n, n a positive integer, {} are literal + + All arguments in the variable list are const char*. Positional + elements are indexed from 1. + */ + static CString format(const char* fmt, ...); + + //! Format positional arguments + /*! + Same as format() except takes va_list. + */ + static CString vformat(const char* fmt, va_list); + + //! Print a string using printf-style formatting + /*! + Equivalent to printf() except the result is returned as a CString. + */ + static CString print(const char* fmt, ...); + + //! Print a string using printf-style formatting + /*! + Same as print() except takes va_list. + */ + static CString vprint(const char* fmt, va_list); + + //! Print a string using printf-style formatting into a buffer + /*! + This is like print but print into a given buffer. If the resulting + string will not fit into \c buffer then a new buffer is allocated and + returned, otherwise \c buffer is returned. the caller must delete[] + the returned memory if is not \c buffer. + + \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and + at least \c suffix characters are available in the buffer before + and after the printed string, respectively. \c bufferLength is the + length of buffer and should not be adjusted by the caller to + account for \c prefix or \c suffix. + */ + static char* vsprint(char* buffer, int bufferLength, + int prefix, int suffix, const char* fmt, va_list); + + //! Case-insensitive comparisons + /*! + This class provides case-insensitve comparison functions. + */ + class CaselessCmp { + public: + //! Same as less() + bool operator()(const CString& a, const CString& b) const; + + //! Returns true iff \c a is lexicographically less than \c b + static bool less(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool equal(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically less than \c b + static bool cmpLess(const CString::value_type& a, + const CString::value_type& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool cmpEqual(const CString::value_type& a, + const CString::value_type& b); + }; +}; + +#endif + diff --git a/lib/io/CUnicode.cpp b/lib/base/CUnicode.cpp similarity index 92% rename from lib/io/CUnicode.cpp rename to lib/base/CUnicode.cpp index 040159f5..a0233d8d 100644 --- a/lib/io/CUnicode.cpp +++ b/lib/base/CUnicode.cpp @@ -13,7 +13,7 @@ */ #include "CUnicode.h" -#include +#include "CArch.h" #include // @@ -70,59 +70,6 @@ setError(bool* errors) } } -// -// multibyte conversion stuff when reentrant versions not available -// - -#if WINDOWS_LIKE -#define HAVE_MBSINIT 1 -#define HAVE_MBRTOWC 1 -#define HAVE_WCRTOMB 1 -#endif - -#if !HAVE_MBSINIT -static -int -mbsinit(const mbstate_t*) -{ - return 1; -} -#endif - -#if !HAVE_MBRTOWC -#include "CLock.h" -#include "CMutex.h" - -static CMutex s_mbrtowcMutex; - -static -size_t -mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t*) -{ - CLock lock(&s_mbrtowcMutex); - int result = mbtowc(pwc, s, n); - if (result < 0) - return (size_t)-1; - else - return result; -} -#endif - -#if !HAVE_WCRTOMB -#include "CLock.h" -#include "CMutex.h" - -static CMutex s_wcrtombMutex; - -static -size_t -wcrtomb(char* s, wchar_t wc, mbstate_t*) -{ - CLock lock(&s_wcrtombMutex); - return (size_t)wctomb(s, wc); -} -#endif - // // CUnicode @@ -274,14 +221,12 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) wchar_t* tmp = UTF8ToWideChar(src, size, errors); // get length of multibyte string - char mbc[MB_LEN_MAX]; - size_t mblen; - mbstate_t state; - memset(&state, 0, sizeof(state)); + int mblen; + CArchMBState state = ARCH->newMBState(); size_t len = 0; UInt32 n = size; for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - mblen = wcrtomb(mbc, *scan, &state); + mblen = ARCH->convWCToMB(NULL, *scan, state); if (mblen == -1) { // unconvertable character setError(errors); @@ -293,11 +238,11 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) } // handle nul terminator - mblen = wcrtomb(mbc, L'\0', &state); + mblen = ARCH->convWCToMB(NULL, L'\0', state); if (mblen != -1) { len += mblen; } - assert(mbsinit(&state) != 0); + assert(ARCH->isInitMBState(state) != 0); // allocate multibyte string char* mbs = new char[len]; @@ -306,7 +251,7 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) char* dst = mbs; n = size; for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - mblen = wcrtomb(dst, *scan, &state); + mblen = ARCH->convWCToMB(dst, *scan, state); if (mblen == -1) { // unconvertable character *dst++ = '?'; @@ -315,7 +260,7 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) dst += mblen; } } - mblen = wcrtomb(dst, L'\0', &state); + mblen = ARCH->convWCToMB(dst, L'\0', state); if (mblen != -1) { // don't include nul terminator dst += mblen - 1; @@ -325,6 +270,7 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) // clean up delete[] mbs; delete[] tmp; + ARCH->closeMBState(state); return text; } @@ -382,19 +328,18 @@ CUnicode::textToUTF8(const CString& src, bool* errors) // get length of multibyte string UInt32 n = src.size(); size_t len = 0; - mbstate_t state; - memset(&state, 0, sizeof(state)); + CArchMBState state = ARCH->newMBState(); for (const char* scan = src.c_str(); n > 0; ) { - size_t mblen = mbrtowc(NULL, scan, n, &state); + int mblen = ARCH->convMBToWC(NULL, scan, n, state); switch (mblen) { - case (size_t)-2: + case -2: // incomplete last character. convert to unknown character. setError(errors); len += 1; n = 0; break; - case (size_t)-1: + case -1: // invalid character. count one unknown character and // start at the next byte. setError(errors); @@ -417,7 +362,7 @@ CUnicode::textToUTF8(const CString& src, bool* errors) break; } } - memset(&state, 0, sizeof(state)); + ARCH->initMBState(state); // allocate wide character string wchar_t* wcs = new wchar_t[len]; @@ -426,15 +371,15 @@ CUnicode::textToUTF8(const CString& src, bool* errors) n = src.size(); wchar_t* dst = wcs; for (const char* scan = src.c_str(); n > 0; ++dst) { - size_t mblen = mbrtowc(dst, scan, n, &state); + int mblen = ARCH->convMBToWC(dst, scan, n, state); switch (mblen) { - case (size_t)-2: + case -2: // incomplete character. convert to unknown character. *dst = (wchar_t)0xfffd; n = 0; break; - case (size_t)-1: + case -1: // invalid character. count one unknown character and // start at the next byte. *dst = (wchar_t)0xfffd; diff --git a/lib/io/CUnicode.h b/lib/base/CUnicode.h similarity index 99% rename from lib/io/CUnicode.h rename to lib/base/CUnicode.h index 19816b17..85afbfd9 100644 --- a/lib/io/CUnicode.h +++ b/lib/base/CUnicode.h @@ -17,7 +17,6 @@ #include "CString.h" #include "BasicTypes.h" -#include //! Unicode utility functions /*! diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h new file mode 100644 index 00000000..b4152c9e --- /dev/null +++ b/lib/base/ILogOutputter.h @@ -0,0 +1,71 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ILOGOUTPUTTER_H +#define ILOGOUTPUTTER_H + +#include "CLog.h" + +//! Outputter interface +/*! +Type of outputter interface. The logger performs all output through +outputters. ILogOutputter overrides must not call any log functions +directly or indirectly. +*/ +class ILogOutputter : public IInterface { +public: + typedef CLog::ELevel ELevel; + + //! @name manipulators + //@{ + + //! Open the outputter + /*! + Opens the outputter for writing. Calling this method on an + already open outputter must have no effect. + */ + virtual void open(const char* title) = 0; + + //! Close the outputter + /*! + Close the outputter. Calling this method on an already closed + outputter must have no effect. + */ + virtual void close() = 0; + + //! Write a message with level + /*! + Writes \c message, which has the given \c level, to a log. + If this method returns true then CLog will stop passing the + message to all outputters in the outputter chain, otherwise + it continues. Most implementations should return true. + */ + virtual bool write(ELevel level, const char* message) = 0; + + //@} + //! @name accessors + //@{ + + //! Returns the newline sequence for the outputter + /*! + Different outputters use different character sequences for newlines. + This method returns the appropriate newline sequence for this + outputter. + */ + virtual const char* getNewline() const = 0; + + //@} +}; + +#endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 19ba6847..a89cf120 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -28,29 +28,23 @@ libbase_a_SOURCES = \ CFunctionJob.cpp \ CLog.cpp \ CStopwatch.cpp \ - CString.cpp \ + CStringUtil.cpp \ + CUnicode.cpp \ + LogOutputters.cpp \ XBase.cpp \ - BasicTypes.h \ CFunctionJob.h \ CLog.h \ CStopwatch.h \ CString.h \ - IInterface.h \ + CStringUtil.h \ + CUnicode.h \ IJob.h \ + ILogOutputter.h \ + LogOutputters.h \ TMethodJob.h \ - Version.h \ XBase.h \ - common.h \ - stdfstream.h \ - stdistream.h \ - stdlist.h \ - stdmap.h \ - stdostream.h \ - stdpost.h \ - stdpre.h \ - stdset.h \ - stdsstream.h \ - stdvector.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ $(NULL) diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp index 057024b0..20d5f464 100644 --- a/lib/base/XBase.cpp +++ b/lib/base/XBase.cpp @@ -13,6 +13,7 @@ */ #include "XBase.h" +#include "CStringUtil.h" #include #include @@ -79,65 +80,3 @@ XBase::format(const char* /*id*/, const char* fmt, ...) const throw() return result; } - - -// -// MXErrno -// - -MXErrno::MXErrno() : -#if WINDOWS_LIKE - m_errno(GetLastError()), -#else - m_errno(errno), -#endif - m_string(NULL) -{ - // do nothing -} - -MXErrno::MXErrno(int err) : - m_errno(err), - m_string(NULL) -{ - // do nothing -} - -MXErrno::~MXErrno() -{ - if (m_string != NULL) { -#if WINDOWS_LIKE - LocalFree(m_string); -#endif - } -} - -int -MXErrno::getErrno() const -{ - return m_errno; -} - -const char* -MXErrno::getErrstr() const -{ -#if WINDOWS_LIKE - if (m_string != NULL) { - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&m_string, - 0, - NULL) == 0) { - m_string = NULL; - return "unknown error"; - } - } - return m_string; -#else - return strerror(m_errno); -#endif -} diff --git a/lib/base/XBase.h b/lib/base/XBase.h index 72a871a4..fa93f94d 100644 --- a/lib/base/XBase.h +++ b/lib/base/XBase.h @@ -16,9 +16,11 @@ #define XBASE_H #include "CString.h" +/* #include "stdpre.h" #include #include "stdpost.h" +*/ //! Exception base class /*! @@ -52,33 +54,73 @@ private: mutable CString m_what; }; -//! Mix-in for handling \c errno /*! -This mix-in class for exception classes provides storage and query of -\c errno. On Windows, it uses GetLastError() instead of errno. +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() is not +declared. */ -class MXErrno { -public: - //! Save \c errno as the error code - MXErrno(); - //! Save \c err as the error code - MXErrno(int err); - virtual ~MXErrno(); +#define XBASE_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ +} - //! @name accessors - //@{ +/*! +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() must be +implemented. +*/ +#define XBASE_SUBCLASS_WHAT(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ +} - //! Get the error code - int getErrno() const; - - //! Get the human readable string for the error code - virtual const char* getErrstr() const; - - //@} - -private: - int m_errno; - mutable char* m_string; -}; +/*! +\def XBASE_SUBCLASS_FORMAT +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. what() is overridden +to call getWhat() when first called; getWhat() can format the +error message and can call what() to get the message passed to the +c'tor. +*/ +#define XBASE_SUBCLASS_FORMAT(name_, super_) \ +class name_ : public super_ { \ +private: \ + enum EState { kFirst, kFormat, kDone }; \ + \ +public: \ + name_() : super_(), m_state(kDone) { } \ + name_(const CString& msg) : super_(msg), m_state(kFirst) { } \ + \ + virtual const char* what() const \ + { \ + if (m_state == kFirst) { \ + m_state = kFormat; \ + m_formatted = getWhat(); \ + m_state = kDone; \ + } \ + if (m_state == kDone) { \ + return m_formatted.c_str(); \ + } \ + else { \ + return super_::what(); \ + } \ + } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ + \ +private: \ + mutable EState m_state; \ + mutable std::string m_formatted; \ +} #endif diff --git a/lib/base/base.dsp b/lib/base/base.dsp index 42a3ec67..a90dabc0 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -99,7 +99,15 @@ SOURCE=.\CStopwatch.cpp # End Source File # Begin Source File -SOURCE=.\CString.cpp +SOURCE=.\CStringUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.cpp +# End Source File +# Begin Source File + +SOURCE=.\LogOutputters.cpp # End Source File # Begin Source File @@ -111,10 +119,6 @@ SOURCE=.\XBase.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\BasicTypes.h -# End Source File -# Begin Source File - SOURCE=.\CFunctionJob.h # End Source File # Begin Source File @@ -123,10 +127,6 @@ SOURCE=.\CLog.h # End Source File # Begin Source File -SOURCE=.\common.h -# End Source File -# Begin Source File - SOURCE=.\CStopwatch.h # End Source File # Begin Source File @@ -135,7 +135,11 @@ SOURCE=.\CString.h # End Source File # Begin Source File -SOURCE=.\IInterface.h +SOURCE=.\CStringUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.h # End Source File # Begin Source File @@ -143,43 +147,11 @@ SOURCE=.\IJob.h # End Source File # Begin Source File -SOURCE=.\stdfstream.h +SOURCE=.\ILogOutputter.h # End Source File # Begin Source File -SOURCE=.\stdistream.h -# End Source File -# Begin Source File - -SOURCE=.\stdlist.h -# End Source File -# Begin Source File - -SOURCE=.\stdmap.h -# End Source File -# Begin Source File - -SOURCE=.\stdostream.h -# End Source File -# Begin Source File - -SOURCE=.\stdpost.h -# End Source File -# Begin Source File - -SOURCE=.\stdpre.h -# End Source File -# Begin Source File - -SOURCE=.\stdset.h -# End Source File -# Begin Source File - -SOURCE=.\stdsstream.h -# End Source File -# Begin Source File - -SOURCE=.\stdvector.h +SOURCE=.\LogOutputters.h # End Source File # Begin Source File @@ -187,10 +159,6 @@ SOURCE=.\TMethodJob.h # End Source File # Begin Source File -SOURCE=.\Version.h -# End Source File -# Begin Source File - SOURCE=.\XBase.h # End Source File # End Group diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index d84ac270..932072e8 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -36,6 +36,7 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" +#include "CArch.h" // // CClient @@ -545,7 +546,7 @@ CClient::runServer() break; } catch (XSocketConnect& e) { - LOG((CLOG_DEBUG1 "failed to connect to server: %s", e.getErrstr())); + LOG((CLOG_DEBUG1 "failed to connect to server: %s", e.what())); // failed to connect. if not camping then rethrow. if (!m_camp) { @@ -553,7 +554,7 @@ CClient::runServer() } // we're camping. wait a bit before retrying - CThread::sleep(15.0); + ARCH->sleep(15.0); } } diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 11536616..7f858323 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -14,10 +14,10 @@ #include "CMSWindowsSecondaryScreen.h" #include "CMSWindowsScreen.h" -#include "CPlatform.h" #include "XScreen.h" #include "CLock.h" #include "CLog.h" +#include "CArchMiscWindows.h" #include // these are only defined when WINVER >= 0x0500 @@ -34,7 +34,7 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( IScreenReceiver* receiver) : - m_is95Family(CPlatform::isWindows95Family()), + m_is95Family(CArchMiscWindows::isWindows95Family()), m_window(NULL), m_mask(0) { diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 28673161..23c0e101 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -38,6 +38,8 @@ libclient_a_SOURCES = \ ISecondaryScreenFactory.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/client/client.dsp b/lib/client/client.dsp index 5880f495..a9b03625 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/base/BasicTypes.h b/lib/common/BasicTypes.h similarity index 100% rename from lib/base/BasicTypes.h rename to lib/common/BasicTypes.h diff --git a/lib/base/IInterface.h b/lib/common/IInterface.h similarity index 100% rename from lib/base/IInterface.h rename to lib/common/IInterface.h diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am new file mode 100644 index 00000000..8a6abc21 --- /dev/null +++ b/lib/common/Makefile.am @@ -0,0 +1,45 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + common.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libcommon.a +libcommon_a_SOURCES = \ + BasicTypes.h \ + IInterface.h \ + Version.h \ + common.h \ + stdfstream.h \ + stdistream.h \ + stdlist.h \ + stdmap.h \ + stdostream.h \ + stdpost.h \ + stdpre.h \ + stdset.h \ + stdsstream.h \ + stdstring.h \ + stdvector.h \ + $(NULL) +INCLUDES = \ + $(NULL) diff --git a/lib/base/Version.h b/lib/common/Version.h similarity index 91% rename from lib/base/Version.h rename to lib/common/Version.h index ebc57b63..882e8f7b 100644 --- a/lib/base/Version.h +++ b/lib/common/Version.h @@ -23,6 +23,7 @@ #endif // important strings +static const char* kApplication = "synergy"; static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; static const char* kWebsite = "http://synergy2.sourceforge.net/"; @@ -31,6 +32,9 @@ static const char* kWebsite = "http://synergy2.sourceforge.net/"; // a release version, odd implies development version. static const char* kVersion = VERSION; +// application version +static const char* kAppVersion = "synergy " VERSION; + // exit codes static const int kExitSuccess = 0; // successful completion static const int kExitFailed = 1; // general failure diff --git a/lib/common/common.dsp b/lib/common/common.dsp new file mode 100644 index 00000000..dd600c0a --- /dev/null +++ b/lib/common/common.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="common" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=common - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "common.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "common.mak" CFG="common - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "common - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "common - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "common - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "common - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "common - Win32 Release" +# Name "common - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BasicTypes.h +# End Source File +# Begin Source File + +SOURCE=.\common.h +# End Source File +# Begin Source File + +SOURCE=.\IInterface.h +# End Source File +# Begin Source File + +SOURCE=.\stdfstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdistream.h +# End Source File +# Begin Source File + +SOURCE=.\stdlist.h +# End Source File +# Begin Source File + +SOURCE=.\stdmap.h +# End Source File +# Begin Source File + +SOURCE=.\stdostream.h +# End Source File +# Begin Source File + +SOURCE=.\stdpost.h +# End Source File +# Begin Source File + +SOURCE=.\stdpre.h +# End Source File +# Begin Source File + +SOURCE=.\stdset.h +# End Source File +# Begin Source File + +SOURCE=.\stdsstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdstring.h +# End Source File +# Begin Source File + +SOURCE=.\stdvector.h +# End Source File +# Begin Source File + +SOURCE=.\Version.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/base/common.h b/lib/common/common.h similarity index 100% rename from lib/base/common.h rename to lib/common/common.h diff --git a/lib/base/stdfstream.h b/lib/common/stdfstream.h similarity index 100% rename from lib/base/stdfstream.h rename to lib/common/stdfstream.h diff --git a/lib/base/stdistream.h b/lib/common/stdistream.h similarity index 100% rename from lib/base/stdistream.h rename to lib/common/stdistream.h diff --git a/lib/base/stdlist.h b/lib/common/stdlist.h similarity index 100% rename from lib/base/stdlist.h rename to lib/common/stdlist.h diff --git a/lib/base/stdmap.h b/lib/common/stdmap.h similarity index 100% rename from lib/base/stdmap.h rename to lib/common/stdmap.h diff --git a/lib/base/stdostream.h b/lib/common/stdostream.h similarity index 100% rename from lib/base/stdostream.h rename to lib/common/stdostream.h diff --git a/lib/base/stdpost.h b/lib/common/stdpost.h similarity index 100% rename from lib/base/stdpost.h rename to lib/common/stdpost.h diff --git a/lib/base/stdpre.h b/lib/common/stdpre.h similarity index 100% rename from lib/base/stdpre.h rename to lib/common/stdpre.h diff --git a/lib/base/stdset.h b/lib/common/stdset.h similarity index 100% rename from lib/base/stdset.h rename to lib/common/stdset.h diff --git a/lib/base/stdsstream.h b/lib/common/stdsstream.h similarity index 100% rename from lib/base/stdsstream.h rename to lib/common/stdsstream.h diff --git a/lib/common/stdstring.h b/lib/common/stdstring.h new file mode 100644 index 00000000..3d83c03c --- /dev/null +++ b/lib/common/stdstring.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/base/stdvector.h b/lib/common/stdvector.h similarity index 100% rename from lib/base/stdvector.h rename to lib/common/stdvector.h diff --git a/lib/http/CHTTPProtocol.h b/lib/http/CHTTPProtocol.h index d4e06b43..8c866455 100644 --- a/lib/http/CHTTPProtocol.h +++ b/lib/http/CHTTPProtocol.h @@ -16,6 +16,7 @@ #define CHTTPPROTOCOL_H #include "CString.h" +#include "CStringUtil.h" #include "BasicTypes.h" #include "stdlist.h" #include "stdmap.h" diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am index ba51677e..c9abe536 100644 --- a/lib/http/Makefile.am +++ b/lib/http/Makefile.am @@ -31,6 +31,8 @@ libhttp_a_SOURCES = \ XHTTP.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/http/XHTTP.cpp b/lib/http/XHTTP.cpp index 493a0383..9b146091 100644 --- a/lib/http/XHTTP.cpp +++ b/lib/http/XHTTP.cpp @@ -14,6 +14,7 @@ #include "XHTTP.h" #include "CHTTPProtocol.h" +#include "CStringUtil.h" #include "stdsstream.h" // diff --git a/lib/http/http.dsp b/lib/http/http.dsp index 5d3a6e7b..439b0616 100644 --- a/lib/http/http.dsp +++ b/lib/http/http.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 3ea4e3af..8bac4c8e 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -30,20 +30,20 @@ libio_a_SOURCES = \ CInputStreamFilter.cpp \ COutputStreamFilter.cpp \ CStreamBuffer.cpp \ - CUnicode.cpp \ XIO.cpp \ CBufferedInputStream.h \ CBufferedOutputStream.h \ CInputStreamFilter.h \ COutputStreamFilter.h \ CStreamBuffer.h \ - CUnicode.h \ IInputStream.h \ IOutputStream.h \ IStreamFilterFactory.h \ XIO.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ $(NULL) diff --git a/lib/io/XIO.h b/lib/io/XIO.h index 3cb1d944..31871bf5 100644 --- a/lib/io/XIO.h +++ b/lib/io/XIO.h @@ -18,33 +18,25 @@ #include "XBase.h" //! Generic I/O exception -class XIO : public XBase { }; +XBASE_SUBCLASS(XIO, XBase); //! I/O closing exception /*! Thrown if a stream cannot be closed. */ -class XIOClose : public XIO { }; +XBASE_SUBCLASS(XIOClose, XIO); //! I/O already closed exception /*! Thrown when attempting to close or perform I/O on an already closed. stream. */ -class XIOClosed : public XIO { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XIOClosed, XIO); //! I/O end of stream exception /*! Thrown when attempting to read beyond the end of a stream. */ -class XIOEndOfStream : public XIO { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XIOEndOfStream, XIO); #endif diff --git a/lib/io/io.dsp b/lib/io/io.dsp index 02159a1a..a6afae06 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -107,10 +107,6 @@ SOURCE=.\CStreamBuffer.cpp # End Source File # Begin Source File -SOURCE=.\CUnicode.cpp -# End Source File -# Begin Source File - SOURCE=.\XIO.cpp # End Source File # End Group @@ -139,10 +135,6 @@ SOURCE=.\CStreamBuffer.h # End Source File # Begin Source File -SOURCE=.\CUnicode.h -# End Source File -# Begin Source File - SOURCE=.\IInputStream.h # End Source File # Begin Source File diff --git a/lib/mt/CCondVar.cpp b/lib/mt/CCondVar.cpp index ae618849..d13aedfd 100644 --- a/lib/mt/CCondVar.cpp +++ b/lib/mt/CCondVar.cpp @@ -14,6 +14,7 @@ #include "CCondVar.h" #include "CStopwatch.h" +#include "CArch.h" // // CCondVarBase @@ -21,17 +22,14 @@ CCondVarBase::CCondVarBase(CMutex* mutex) : m_mutex(mutex) -#if WINDOWS_LIKE - , m_waitCountMutex() -#endif { assert(m_mutex != NULL); - init(); + m_cond = ARCH->newCondVar(); } CCondVarBase::~CCondVarBase() { - fini(); + ARCH->closeCondVar(m_cond); } void @@ -46,67 +44,16 @@ CCondVarBase::unlock() const m_mutex->unlock(); } -bool -CCondVarBase::wait(double timeout) const -{ - CStopwatch timer(true); - return wait(timer, timeout); -} - -CMutex* -CCondVarBase::getMutex() const -{ - return m_mutex; -} - -#if HAVE_PTHREAD - -#include "CThread.h" -#include -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif -#include - -void -CCondVarBase::init() -{ - pthread_cond_t* cond = new pthread_cond_t; - int status = pthread_cond_init(cond, NULL); - assert(status == 0); - m_cond = reinterpret_cast(cond); -} - -void -CCondVarBase::fini() -{ - pthread_cond_t* cond = reinterpret_cast(m_cond); - int status = pthread_cond_destroy(cond); - assert(status == 0); - delete cond; -} - void CCondVarBase::signal() { - pthread_cond_t* cond = reinterpret_cast(m_cond); - int status = pthread_cond_signal(cond); - assert(status == 0); + ARCH->signalCondVar(m_cond); } void CCondVarBase::broadcast() { - pthread_cond_t* cond = reinterpret_cast(m_cond); - int status = pthread_cond_broadcast(cond); - assert(status == 0); + ARCH->broadcastCondVar(m_cond); } bool @@ -118,221 +65,17 @@ CCondVarBase::wait(CStopwatch& timer, double timeout) const if (timeout < 0.0) return false; } - - // get condition variable and mutex - pthread_cond_t* cond = reinterpret_cast(m_cond); - pthread_mutex_t* mutex = reinterpret_cast(m_mutex->m_mutex); - - // get final time - struct timeval now; - gettimeofday(&now, NULL); - struct timespec finalTime; - finalTime.tv_sec = now.tv_sec; - finalTime.tv_nsec = now.tv_usec * 1000; - if (timeout >= 0.0) { - const long timeout_sec = (long)timeout; - const long timeout_nsec = (long)(1000000000.0 * (timeout - timeout_sec)); - finalTime.tv_sec += timeout_sec; - finalTime.tv_nsec += timeout_nsec; - if (finalTime.tv_nsec >= 1000000000) { - finalTime.tv_nsec -= 1000000000; - finalTime.tv_sec += 1; - } - } - - // repeat until we reach the final time - int status; - for (;;) { - // compute the next timeout - gettimeofday(&now, NULL); - struct timespec endTime; - endTime.tv_sec = now.tv_sec; - endTime.tv_nsec = now.tv_usec * 1000 + 50000000; - if (endTime.tv_nsec >= 1000000000) { - endTime.tv_nsec -= 1000000000; - endTime.tv_sec += 1; - } - - // see if we should cancel this thread - CThread::testCancel(); - - // done if past final timeout - if (timeout >= 0.0) { - if (endTime.tv_sec > finalTime.tv_sec || - (endTime.tv_sec == finalTime.tv_sec && - endTime.tv_nsec >= finalTime.tv_nsec)) { - status = ETIMEDOUT; - break; - } - } - - // wait - status = pthread_cond_timedwait(cond, mutex, &endTime); - - // check for cancel again - CThread::testCancel(); - - // check wait status - if (status != ETIMEDOUT && status != EINTR) { - break; - } - } - - switch (status) { - case 0: - // success - return true; - - case ETIMEDOUT: - return false; - - default: - assert(0 && "condition variable wait error"); - return false; - } -} - -#endif // HAVE_PTHREAD - -#if WINDOWS_LIKE - -#include "CLock.h" -#include "CThreadRep.h" -#define WIN32_LEAN_AND_MEAN -#include - -// -// note -- implementation taken from -// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html -// titled "Strategies for Implementing POSIX Condition Variables -// on Win32." it also provides an implementation that doesn't -// suffer from the incorrectness problem described in our -// corresponding header but it is slower, still unfair, and -// can cause busy waiting. -// - -void -CCondVarBase::init() -{ - // prepare events - HANDLE* events = new HANDLE[2]; - events[kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL); - events[kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL); - - // prepare members - m_cond = reinterpret_cast(events); - m_waitCount = 0; -} - -void -CCondVarBase::fini() -{ - HANDLE* events = reinterpret_cast(m_cond); - CloseHandle(events[kSignal]); - CloseHandle(events[kBroadcast]); - delete[] events; -} - -void -CCondVarBase::signal() -{ - // is anybody waiting? - bool hasWaiter; - { - CLock lock(&m_waitCountMutex); - hasWaiter = (m_waitCount > 0); - } - - // wake one thread if anybody is waiting - if (hasWaiter) { - SetEvent(reinterpret_cast(m_cond)[kSignal]); - } -} - -void -CCondVarBase::broadcast() -{ - // is anybody waiting? - bool hasWaiter; - { - CLock lock(&m_waitCountMutex); - hasWaiter = (m_waitCount > 0); - } - - // wake all threads if anybody is waiting - if (hasWaiter) { - SetEvent(reinterpret_cast(m_cond)[kBroadcast]); - } + return wait(timeout); } bool -CCondVarBase::wait(CStopwatch& timer, double timeout) const +CCondVarBase::wait(double timeout) const { - // check timeout against timer - if (timeout >= 0.0) { - timeout -= timer.getTime(); - if (timeout < 0.0) { - return false; - } - } - - // prepare to wait - CThreadPtr currentRep = CThreadRep::getCurrentThreadRep(); - const DWORD winTimeout = (timeout < 0.0) ? INFINITE : - static_cast(1000.0 * timeout); - HANDLE* events = reinterpret_cast(m_cond); - HANDLE handles[3]; - handles[0] = events[kSignal]; - handles[1] = events[kBroadcast]; - handles[2] = currentRep->getCancelEvent(); - const DWORD n = currentRep->isCancellable() ? 3 : 2; - - // update waiter count - { - CLock lock(&m_waitCountMutex); - ++m_waitCount; - } - - // release mutex. this should be atomic with the wait so that it's - // impossible for another thread to signal us between the unlock and - // the wait, which would lead to a lost signal on broadcasts. - // however, we're using a manual reset event for broadcasts which - // stays set until we reset it, so we don't lose the broadcast. - m_mutex->unlock(); - - // wait for a signal or broadcast - DWORD result = WaitForMultipleObjects(n, handles, FALSE, winTimeout); - - // cancel takes priority - if (n == 3 && result != WAIT_OBJECT_0 + 2 && - WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) { - result = WAIT_OBJECT_0 + 2; - } - - // update the waiter count and check if we're the last waiter - bool last; - { - CLock lock(&m_waitCountMutex); - --m_waitCount; - last = (result == WAIT_OBJECT_0 + 1 && m_waitCount == 0); - } - - // reset the broadcast event if we're the last waiter - if (last) { - ResetEvent(events[kBroadcast]); - } - - // reacquire the mutex - m_mutex->lock(); - - // cancel thread if necessary - if (result == WAIT_OBJECT_0 + 2) { - currentRep->testCancel(); - } - - // return success or failure - return (result == WAIT_OBJECT_0 + 0 || - result == WAIT_OBJECT_0 + 1); + return ARCH->waitCondVar(m_cond, m_mutex->m_mutex, timeout); } -#endif // WINDOWS_LIKE +CMutex* +CCondVarBase::getMutex() const +{ + return m_mutex; +} diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h index 5310e148..1b92ffd7 100644 --- a/lib/mt/CCondVar.h +++ b/lib/mt/CCondVar.h @@ -17,6 +17,7 @@ #include "CMutex.h" #include "BasicTypes.h" +#include "IArchMultithread.h" class CStopwatch; @@ -112,22 +113,13 @@ public: //@} private: - void init(); - void fini(); - // not implemented CCondVarBase(const CCondVarBase&); CCondVarBase& operator=(const CCondVarBase&); private: CMutex* m_mutex; - void* m_cond; - -#if WINDOWS_LIKE - enum { kSignal, kBroadcast }; - mutable UInt32 m_waitCount; - CMutex m_waitCountMutex; -#endif + CArchCond m_cond; }; //! Condition variable diff --git a/lib/mt/CMutex.cpp b/lib/mt/CMutex.cpp index 6e51ea7e..0d2bab3c 100644 --- a/lib/mt/CMutex.cpp +++ b/lib/mt/CMutex.cpp @@ -13,7 +13,7 @@ */ #include "CMutex.h" -#include "CLog.h" +#include "CArch.h" // // CMutex @@ -21,17 +21,17 @@ CMutex::CMutex() { - init(); + m_mutex = ARCH->newMutex(); } CMutex::CMutex(const CMutex&) { - init(); + m_mutex = ARCH->newMutex(); } CMutex::~CMutex() { - fini(); + ARCH->closeMutex(m_mutex); } CMutex& @@ -40,111 +40,14 @@ CMutex::operator=(const CMutex&) return *this; } -#if HAVE_PTHREAD - -#include -#include - -void -CMutex::init() -{ - pthread_mutex_t* mutex = new pthread_mutex_t; - int status = pthread_mutex_init(mutex, NULL); - assert(status == 0); -// status = pthread_mutexattr_settype(mutex, PTHREAD_MUTEX_RECURSIVE); -// assert(status == 0); - m_mutex = reinterpret_cast(mutex); -} - -void -CMutex::fini() -{ - pthread_mutex_t* mutex = reinterpret_cast(m_mutex); - int status = pthread_mutex_destroy(mutex); - LOGC(status != 0, (CLOG_ERR "pthread_mutex_destroy status %d", status)); - assert(status == 0); - delete mutex; -} - void CMutex::lock() const { - pthread_mutex_t* mutex = reinterpret_cast(m_mutex); - int status = pthread_mutex_lock(mutex); - - switch (status) { - case 0: - // success - return; - - case EDEADLK: - assert(0 && "lock already owned"); - break; - - case EAGAIN: - assert(0 && "too many recursive locks"); - break; - - default: - LOG((CLOG_ERR "pthread_mutex_lock status %d", status)); - assert(0 && "unexpected error"); - } + ARCH->lockMutex(m_mutex); } void CMutex::unlock() const { - pthread_mutex_t* mutex = reinterpret_cast(m_mutex); - int status = pthread_mutex_unlock(mutex); - - switch (status) { - case 0: - // success - return; - - case EPERM: - assert(0 && "thread doesn't own a lock"); - break; - - default: - LOG((CLOG_ERR "pthread_mutex_unlock status %d", status)); - assert(0 && "unexpected error"); - } + ARCH->unlockMutex(m_mutex); } - -#endif // HAVE_PTHREAD - -#if WINDOWS_LIKE - -#define WIN32_LEAN_AND_MEAN -#include - -void -CMutex::init() -{ - CRITICAL_SECTION* mutex = new CRITICAL_SECTION; - InitializeCriticalSection(mutex); - m_mutex = reinterpret_cast(mutex); -} - -void -CMutex::fini() -{ - CRITICAL_SECTION* mutex = reinterpret_cast(m_mutex); - DeleteCriticalSection(mutex); - delete mutex; -} - -void -CMutex::lock() const -{ - EnterCriticalSection(reinterpret_cast(m_mutex)); -} - -void -CMutex::unlock() const -{ - LeaveCriticalSection(reinterpret_cast(m_mutex)); -} - -#endif // WINDOWS_LIKE diff --git a/lib/mt/CMutex.h b/lib/mt/CMutex.h index 4ae6fa03..c11eeee3 100644 --- a/lib/mt/CMutex.h +++ b/lib/mt/CMutex.h @@ -15,6 +15,8 @@ #ifndef CMUTEX_H #define CMUTEX_H +#include "IArchMultithread.h" + //! Mutual exclusion /*! A non-recursive mutual exclusion object. Only one thread at a time can @@ -68,13 +70,9 @@ public: //@} -private: - void init(); - void fini(); - private: friend class CCondVarBase; - void* m_mutex; + CArchMutex m_mutex; }; #endif diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index 5afd3122..27b0cedd 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -13,161 +13,166 @@ */ #include "CThread.h" -#include "CLock.h" -#include "CThreadRep.h" +#include "XMT.h" #include "XThread.h" #include "CLog.h" -#include "CStopwatch.h" +#include "IJob.h" +#include "CArch.h" // // CThread // -CThread::CThread(IJob* job, void* userData) +CThread::CThread(IJob* job) { - m_rep = new CThreadRep(job, userData); + m_thread = ARCH->newThread(&CThread::threadFunc, job); + if (m_thread == NULL) { + // couldn't create thread + delete job; + throw XMTThreadUnavailable(); + } } -CThread::CThread(const CThread& thread) : - m_rep(thread.m_rep) +CThread::CThread(const CThread& thread) { - m_rep->ref(); + m_thread = ARCH->copyThread(thread.m_thread); } -CThread::CThread(CThreadRep* rep) : - m_rep(rep) +CThread::CThread(CArchThread adoptedThread) { - // do nothing. rep should have already been Ref()'d. + m_thread = adoptedThread; } CThread::~CThread() { - m_rep->unref(); + ARCH->closeThread(m_thread); } CThread& CThread::operator=(const CThread& thread) { - if (thread.m_rep != m_rep) { - m_rep->unref(); - m_rep = thread.m_rep; - m_rep->ref(); - } + // copy given thread and release ours + CArchThread copy = ARCH->copyThread(thread.m_thread); + ARCH->closeThread(m_thread); + + // cut over + m_thread = copy; + return *this; } -void -CThread::init() -{ - CThreadRep::initThreads(); -} - -void -CThread::sleep(double timeout) -{ - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - if (timeout >= 0.0) { - currentRep->testCancel(); - currentRep->sleep(timeout); - } - currentRep->testCancel(); -} - void CThread::exit(void* result) { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - LOG((CLOG_DEBUG1 "throw exit on thread %p", currentRep.operator->())); throw XThreadExit(result); } -bool -CThread::enableCancel(bool enable) -{ - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->enableCancel(enable); -} - void CThread::cancel() { - m_rep->cancel(); + ARCH->cancelThread(m_thread); } void CThread::setPriority(int n) { - m_rep->setPriority(n); + ARCH->setPriorityOfThread(m_thread, n); } CThread CThread::getCurrentThread() { - return CThread(CThreadRep::getCurrentThreadRep()); + return CThread(ARCH->newCurrentThread()); +} + +void +CThread::testCancel() +{ + ARCH->testCancelThread(); } bool CThread::wait(double timeout) const { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->wait(m_rep, timeout); + return ARCH->wait(m_thread, timeout); } -#if WINDOWS_LIKE bool CThread::waitForEvent(double timeout) { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->waitForEvent(timeout); -} -#endif - -void -CThread::testCancel() -{ - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - currentRep->testCancel(); + return ARCH->waitForEvent(timeout); } void* CThread::getResult() const { if (wait()) - return m_rep->getResult(); + return ARCH->getResultOfThread(m_thread); else return NULL; } -void* -CThread::getUserData() +IArchMultithread::ThreadID +CThread::getID() const { - return m_rep->getUserData(); + return ARCH->getIDOfThread(m_thread); } bool CThread::operator==(const CThread& thread) const { - return (m_rep == thread.m_rep); + return ARCH->isSameThread(m_thread, thread.m_thread); } bool CThread::operator!=(const CThread& thread) const { - return (m_rep != thread.m_rep); + return !ARCH->isSameThread(m_thread, thread.m_thread); } - -// -// CThreadMaskCancel -// - -CThreadMaskCancel::CThreadMaskCancel() : - m_old(CThread::enableCancel(false)) +void* +CThread::threadFunc(void* vjob) { - // do nothing -} + // get this thread's id for logging + IArchMultithread::ThreadID id; + { + CArchThread thread = ARCH->newCurrentThread(); + id = ARCH->getIDOfThread(thread); + ARCH->closeThread(thread); + } -CThreadMaskCancel::~CThreadMaskCancel() -{ - CThread::enableCancel(m_old); + // get job + IJob* job = reinterpret_cast(vjob); + + // run job + void* result = NULL; + try { + // go + LOG((CLOG_DEBUG1 "thread 0x%08x entry", id)); + job->run(); + LOG((CLOG_DEBUG1 "thread 0x%08x exit", id)); + } + + catch (XThreadCancel&) { + // client called cancel() + LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id)); + delete job; + throw; + } + catch (XThreadExit& e) { + // client called exit() + result = e.m_result; + LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); + } + catch (...) { + LOG((CLOG_DEBUG1 "exception on thread 0x%08x", id)); + delete job; + throw; + } + + // done with job + delete job; + + // return exit result + return result; } diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index f9dc9ac7..aaa5a097 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -16,9 +16,9 @@ #define CTHREAD_H #include "common.h" +#include "IArchMultithread.h" class IJob; -class CThreadRep; //! Thread handle /*! @@ -43,10 +43,9 @@ public: //! Run \c adoptedJob in a new thread /*! Create and start a new thread executing the \c adoptedJob. The - user data can be retrieved with getUserData(). The new thread - takes ownership of \c adoptedJob and will delete it. + new thread takes ownership of \c adoptedJob and will delete it. */ - CThread(IJob* adoptedJob, void* userData = 0); + CThread(IJob* adoptedJob); //! Duplicate a thread handle /*! @@ -74,24 +73,6 @@ public: */ CThread& operator=(const CThread&); - //! Initialize the thread library - /*! - Initialize the thread library. This \b must be called before - any other thread methods or creating a thread object. It is - harmless to call init() multiple times. - */ - static void init(); - - //! Sleep - /*! - Blocks the calling thread for \c timeout seconds. If - \c timeout < 0.0 then the call returns immediately. If \c timeout - == 0.0 then the calling thread yields the CPU. - - (cancellation point) - */ - static void sleep(double timeout); - //! Terminate the calling thread /*! Terminate the calling thread. This function does not return but @@ -107,15 +88,6 @@ public: */ static void exit(void*); - //! Enable or disable cancellation - /*! - Enable or disable cancellation. The default is enabled. This is not - a cancellation point so if you just enabled cancellation and want to - allow immediate cancellation you need to call testCancel(). - Returns the previous state. - */ - static bool enableCancel(bool); - //! Cancel thread /*! Cancel the thread. cancel() never waits for the thread to @@ -173,12 +145,6 @@ public: */ static void testCancel(); - //! Get the thread user data - /*! - Gets the user data passed to the c'tor that created this thread. - */ - void* getUserData(); - //! Wait for thread to terminate /*! Waits for the thread to terminate (by exit() or cancel() or @@ -192,7 +158,6 @@ public: */ bool wait(double timeout = -1.0) const; -#if WINDOWS_LIKE //! Wait for an event (win32) /*! Wait for the message queue to contain a message for up to \c timeout @@ -207,7 +172,6 @@ public: (cancellation point) */ static bool waitForEvent(double timeout = -1.0); -#endif //! Get the exit result /*! @@ -219,6 +183,15 @@ public: */ void* getResult() const; + //! Get the thread id + /*! + Returns an integer id for this thread. This id must not be used to + check if two CThread objects refer to the same thread. Use + operator==() for that. + */ + IArchMultithread::ThreadID + getID() const; + //! Compare thread handles /*! Returns true if two CThread objects refer to the same thread. @@ -234,24 +207,12 @@ public: //@} private: - CThread(CThreadRep*); + CThread(CArchThread); + + static void* threadFunc(void*); private: - CThreadRep* m_rep; -}; - -//! Disable cancellation utility -/*! -This class disables cancellation for the current thread in the c'tor -and enables it in the d'tor. -*/ -class CThreadMaskCancel { -public: - CThreadMaskCancel(); - ~CThreadMaskCancel(); - -private: - bool m_old; + CArchThread m_thread; }; #endif diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp deleted file mode 100644 index cf1c8276..00000000 --- a/lib/mt/CThreadRep.cpp +++ /dev/null @@ -1,794 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CThreadRep.h" -#include "CLock.h" -#include "CMutex.h" -#include "CThread.h" -#include "XMT.h" -#include "XThread.h" -#include "CLog.h" -#include "IJob.h" - -#if HAVE_PTHREAD - -# include -# define SIGWAKEUP SIGUSR1 - -#elif WINDOWS_LIKE - -# if !defined(_MT) -# error multithreading compile option is required -# endif -# include - -#else - -#error unsupported platform for multithreading - -#endif - -// -// CThreadRep -// - -CMutex* CThreadRep::s_mutex = NULL; -CThreadRep* CThreadRep::s_head = NULL; -#if HAVE_PTHREAD -pthread_t CThreadRep::s_signalThread; -#endif - -CThreadRep::CThreadRep() : - m_prev(NULL), - m_next(NULL), - m_refCount(1), - m_job(NULL), - m_userData(NULL) -{ - // note -- s_mutex must be locked on entry - assert(s_mutex != NULL); - - // initialize stuff - init(); -#if HAVE_PTHREAD - // get main thread id - m_thread = pthread_self(); -#elif WINDOWS_LIKE - // get main thread id - m_thread = NULL; - m_id = GetCurrentThreadId(); -#endif - - // insert ourself into linked list - if (s_head != NULL) { - s_head->m_prev = this; - m_next = s_head; - } - s_head = this; -} - -CThreadRep::CThreadRep(IJob* job, void* userData) : - m_prev(NULL), - m_next(NULL), - m_refCount(2), // 1 for us, 1 for thread - m_job(job), - m_userData(userData) -{ - assert(m_job != NULL); - assert(s_mutex != NULL); - - // create a thread rep for the main thread if the current thread - // is unknown. note that this might cause multiple "main" threads - // if threads are created external to this library. - getCurrentThreadRep()->unref(); - - // initialize - init(); - - // hold mutex while we create the thread - CLock lock(s_mutex); - - // start the thread. throw if it doesn't start. -#if HAVE_PTHREAD - // mask some signals in all threads except the main thread - sigset_t sigset, oldsigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); - // pthread_create() RedHat 7.2 smp fails with a NULL attr. - pthread_attr_t attr; - int status = pthread_attr_init(&attr); - if (status == 0) { - status = pthread_create(&m_thread, &attr, threadFunc, (void*)this); - pthread_attr_destroy(&attr); - } - pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); - if (status != 0) { - throw XMTThreadUnavailable(); - } -#elif WINDOWS_LIKE - unsigned int id; - m_thread = reinterpret_cast(_beginthreadex(NULL, 0, - threadFunc, (void*)this, 0, &id)); - m_id = static_cast(id); - if (m_thread == 0) { - throw XMTThreadUnavailable(); - } -#endif - - // insert ourself into linked list - if (s_head != NULL) { - s_head->m_prev = this; - m_next = s_head; - } - s_head = this; - - // returning releases the locks, allowing the child thread to run -} - -CThreadRep::~CThreadRep() -{ - // note -- s_mutex must be locked on entry - - // remove ourself from linked list - if (m_prev != NULL) { - m_prev->m_next = m_next; - } - if (m_next != NULL) { - m_next->m_prev = m_prev; - } - if (s_head == this) { - s_head = m_next; - } - - // clean up - fini(); -} - -void -CThreadRep::initThreads() -{ - if (s_mutex == NULL) { - s_mutex = new CMutex; - -#if HAVE_PTHREAD - // install SIGWAKEUP handler - struct sigaction act; - sigemptyset(&act.sa_mask); -# if defined(SA_INTERRUPT) - act.sa_flags = SA_INTERRUPT; -# else - act.sa_flags = 0; -# endif - act.sa_handler = &threadCancel; - sigaction(SIGWAKEUP, &act, NULL); - - // set signal mask - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGWAKEUP); - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - sigemptyset(&sigset); - sigaddset(&sigset, SIGPIPE); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_BLOCK, &sigset, NULL); - - // fire up the INT and TERM signal handler thread. we could - // instead arrange to catch and handle these signals but - // we'd be unable to cancel the main thread since no pthread - // calls are allowed in a signal handler. - pthread_attr_t attr; - int status = pthread_attr_init(&attr); - if (status == 0) { - status = pthread_create(&s_signalThread, &attr, - &CThreadRep::threadSignalHandler, - getCurrentThreadRep()); - pthread_attr_destroy(&attr); - } - if (status != 0) { - // can't create thread to wait for signal so don't block - // the signals. - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - } -#endif // HAVE_PTHREAD - } -} - -void -CThreadRep::ref() -{ - CLock lock(s_mutex); - ++m_refCount; -} - -void -CThreadRep::unref() -{ - CLock lock(s_mutex); - if (--m_refCount == 0) { - delete this; - } -} - -bool -CThreadRep::enableCancel(bool enable) -{ - CLock lock(s_mutex); - const bool old = m_cancellable; - m_cancellable = enable; - return old; -} - -bool -CThreadRep::isCancellable() const -{ - CLock lock(s_mutex); - return (m_cancellable && !m_cancelling); -} - -void* -CThreadRep::getResult() const -{ - // no lock necessary since thread isn't running - return m_result; -} - -void* -CThreadRep::getUserData() const -{ - // no lock necessary because the value never changes - return m_userData; -} - -CThreadRep* -CThreadRep::getCurrentThreadRep() -{ - assert(s_mutex != NULL); - -#if HAVE_PTHREAD - const pthread_t thread = pthread_self(); -#elif WINDOWS_LIKE - const DWORD id = GetCurrentThreadId(); -#endif - - // lock list while we search - CLock lock(s_mutex); - - // search - CThreadRep* scan = s_head; - while (scan != NULL) { -#if HAVE_PTHREAD - if (scan->m_thread == thread) { - break; - } -#elif WINDOWS_LIKE - if (scan->m_id == id) { - break; - } -#endif - scan = scan->m_next; - } - - // create and use main thread rep if thread not found - if (scan == NULL) { - scan = new CThreadRep(); - } - - // ref for caller - ++scan->m_refCount; - - return scan; -} - -void -CThreadRep::doThreadFunc() -{ - // default priority is slightly below normal - setPriority(1); - - // wait for parent to initialize this object - { CLock lock(s_mutex); } - - void* result = NULL; - try { - // go - LOG((CLOG_DEBUG1 "thread %p entry", this)); - m_job->run(); - LOG((CLOG_DEBUG1 "thread %p exit", this)); - } - - catch (XThreadCancel&) { - // client called cancel() - LOG((CLOG_DEBUG1 "caught cancel on thread %p", this)); - } - - catch (XThreadExit& e) { - // client called exit() - result = e.m_result; - LOG((CLOG_DEBUG1 "caught exit on thread %p", this)); - } - catch (...) { - LOG((CLOG_DEBUG1 "exception on thread %p", this)); - // note -- don't catch (...) to avoid masking bugs - delete m_job; - throw; - } - - // done with job - delete m_job; - - // store exit result (no lock necessary because the result will - // not be accessed until m_exit is set) - m_result = result; -} - -#if HAVE_PTHREAD - -#include "CStopwatch.h" -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif -#if !HAVE_NANOSLEEP -# if HAVE_SYS_SELECT_H -# include -# endif -# if HAVE_SYS_TYPES_H -# include -# endif -# if HAVE_UNISTD_H -# include -# endif -#endif - -void -CThreadRep::init() -{ - m_result = NULL; - m_cancellable = true; - m_cancelling = false; - m_cancel = false; - m_exit = false; -} - -void -CThreadRep::fini() -{ - // main thread has NULL job - if (m_job != NULL) { - pthread_detach(m_thread); - } -} - -void -CThreadRep::sleep( - double timeout) -{ - if (timeout < 0.0) { - return; - } -#if HAVE_NANOSLEEP - struct timespec t; - t.tv_sec = (long)timeout; - t.tv_nsec = (long)(1000000000.0 * (timeout - (double)t.tv_sec)); - while (nanosleep(&t, &t) < 0) - testCancel(); -#else - /* emulate nanosleep() with select() */ - CStopwatch timer(true); - double timeLeft = timeout - timer.getTime(); - while (timeLeft > 0.0) { - struct timeval timeout2; - timeout2.tv_sec = static_cast(timeLeft); - timeout2.tv_usec = static_cast(1.0e+6 * (timeLeft - - timeout2.tv_sec)); - select((SELECT_TYPE_ARG1) 0, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG5 &timeout2); - testCancel(); - timeLeft = timeout - timer.getTime(); - } -#endif -} - -void -CThreadRep::cancel() -{ - CLock lock(s_mutex); - if (!m_exit && m_cancellable && !m_cancelling) { - m_cancel = true; - } - else { - return; - } - - // break out of system calls - LOG((CLOG_DEBUG1 "cancel thread %p", this)); - pthread_kill(m_thread, SIGWAKEUP); -} - -void -CThreadRep::testCancel() -{ - { - CLock lock(s_mutex); - - // done if not cancelled, not cancellable, or already cancelling - if (!m_cancel || !m_cancellable || m_cancelling) { - return; - } - - // update state for cancel - m_cancel = false; - m_cancelling = true; - } - - // start cancel - LOG((CLOG_DEBUG1 "throw cancel on thread %p", this)); - throw XThreadCancel(); -} - -bool -CThreadRep::wait(CThreadRep* target, double timeout) -{ - if (target == this) { - return false; - } - - testCancel(); - if (target->isExited()) { - return true; - } - - if (timeout != 0.0) { - CStopwatch timer; - do { - sleep(0.05); - testCancel(); - if (target->isExited()) { - return true; - } - } while (timeout < 0.0 || timer.getTime() <= timeout); - } - - return false; -} - -void -CThreadRep::setPriority(int) -{ - // FIXME -} - -bool -CThreadRep::isExited() const -{ - CLock lock(s_mutex); - return m_exit; -} - -void* -CThreadRep::threadFunc(void* arg) -{ - CThreadRep* rep = (CThreadRep*)arg; - - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); - - // run thread - rep->doThreadFunc(); - - { - // mark as terminated - CLock lock(s_mutex); - rep->m_exit = true; - } - - // unref thread - rep->unref(); - - // terminate the thread - return NULL; -} - -void -CThreadRep::threadCancel(int) -{ - // do nothing -} - -void* -CThreadRep::threadSignalHandler(void* vrep) -{ - CThreadRep* mainThreadRep = reinterpret_cast(vrep); - - // detach - pthread_detach(pthread_self()); - - // add signal to mask - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - - // also wait on SIGABRT. on linux (others?) this thread (process) - // will persist after all the other threads evaporate due to an - // assert unless we wait on SIGABRT. that means our resources (like - // the socket we're listening on) are not released and never will be - // until the lingering thread is killed. i don't know why sigwait() - // should protect the thread from being killed. note that sigwait() - // doesn't actually return if we receive SIGABRT and, for some - // reason, we don't have to block SIGABRT. - sigaddset(&sigset, SIGABRT); - - // we exit the loop via thread cancellation in sigwait() - for (;;) { - // wait -#if HAVE_POSIX_SIGWAIT - int signal; - sigwait(&sigset, &signal); -#else - sigwait(&sigset); -#endif - - // if we get here then the signal was raised. cancel the thread. - mainThreadRep->cancel(); - } -} - -#endif // HAVE_PTHREAD - -#if WINDOWS_LIKE - -void -CThreadRep::init() -{ - m_result = NULL; - m_cancellable = true; - m_cancelling = false; - m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); - m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); -} - -void -CThreadRep::fini() -{ - // destroy the events - CloseHandle(m_cancel); - CloseHandle(m_exit); - - // close the handle (main thread has a NULL handle) - if (m_thread != NULL) { - CloseHandle(m_thread); - } -} - -void -CThreadRep::sleep( - double timeout) -{ - if (isCancellable()) { - WaitForSingleObject(m_cancel, (DWORD)(1000.0 * timeout)); - } - else { - Sleep((DWORD)(1000.0 * timeout)); - } -} - -void -CThreadRep::cancel() -{ - LOG((CLOG_DEBUG1 "cancel thread %p", this)); - SetEvent(m_cancel); -} - -void -CThreadRep::testCancel() -{ - // poll cancel event. return if not set. - const DWORD result = WaitForSingleObject(getCancelEvent(), 0); - if (result != WAIT_OBJECT_0) { - return; - } - - { - // ignore if disabled or already cancelling - CLock lock(s_mutex); - if (!m_cancellable || m_cancelling) - return; - - // update state for cancel - m_cancelling = true; - ResetEvent(m_cancel); - } - - // start cancel - LOG((CLOG_DEBUG1 "throw cancel on thread %p", this)); - throw XThreadCancel(); -} - -bool -CThreadRep::wait(CThreadRep* target, double timeout) -{ - // get the current thread. if it's the same as the target thread - // then the thread is waiting on itself. - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - if (target == this) { - return false; - } - - // is cancellation enabled? - const DWORD n = (isCancellable() ? 2 : 1); - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for the target thread to - // terminate. - HANDLE handles[2]; - handles[0] = target->getExitEvent(); - handles[1] = m_cancel; - DWORD result = WaitForMultipleObjects(n, handles, FALSE, t); - - // cancel takes priority - if (n == 2 && result != WAIT_OBJECT_0 + 1 && - WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) { - result = WAIT_OBJECT_0 + 1; - } - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 0: - // target thread terminated - return true; - - case WAIT_OBJECT_0 + 1: - // this thread was cancelled. does not return. - testCancel(); - - default: - // timeout or error - return false; - } -} - -bool -CThreadRep::waitForEvent(double timeout) -{ - // check if messages are available first. if we don't do this then - // MsgWaitForMultipleObjects() will block even if the queue isn't - // empty if the messages in the queue were there before the last - // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { - return true; - } - - // is cancellation enabled? - const DWORD n = (isCancellable() ? 1 : 0); - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for the target thread to - // terminate. - HANDLE handles[1]; - handles[0] = m_cancel; - DWORD result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 1: - // message is available - return true; - - case WAIT_OBJECT_0 + 0: - // this thread was cancelled. does not return. - testCancel(); - - default: - // timeout or error - return false; - } -} - -void -CThreadRep::setPriority(int n) -{ - DWORD pClass = NORMAL_PRIORITY_CLASS; - if (n < 0) { - switch (-n) { - case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - case 2: n = THREAD_PRIORITY_HIGHEST; break; - default: - pClass = HIGH_PRIORITY_CLASS; - switch (-n - 3) { - case 0: n = THREAD_PRIORITY_LOWEST; break; - case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; - case 2: n = THREAD_PRIORITY_NORMAL; break; - case 3: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - default: n = THREAD_PRIORITY_HIGHEST; break; - } - break; - } - } - else { - switch (n) { - case 0: n = THREAD_PRIORITY_NORMAL; break; - case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; - case 2: n = THREAD_PRIORITY_LOWEST; break; - default: n = THREAD_PRIORITY_IDLE; break; - } - } - SetPriorityClass(m_thread, pClass); - SetThreadPriority(m_thread, n); -} - -HANDLE -CThreadRep::getExitEvent() const -{ - // no lock necessary because the value never changes - return m_exit; -} - -HANDLE -CThreadRep::getCancelEvent() const -{ - // no lock necessary because the value never changes - return m_cancel; -} - -unsigned int __stdcall -CThreadRep::threadFunc(void* arg) -{ - CThreadRep* rep = (CThreadRep*)arg; - - // run thread - rep->doThreadFunc(); - - // signal termination - SetEvent(rep->m_exit); - - // unref thread - rep->unref(); - - // terminate the thread - return 0; -} - -#endif // WINDOWS_LIKE diff --git a/lib/mt/CThreadRep.h b/lib/mt/CThreadRep.h deleted file mode 100644 index 13ea27be..00000000 --- a/lib/mt/CThreadRep.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CTHREADREP_H -#define CTHREADREP_H - -#include "common.h" - -#if HAVE_PTHREAD -#include -#elif WINDOWS_LIKE -#define WIN32_LEAN_AND_MEAN -#include -#endif - -class CMutex; -class IJob; - -//! Internal thread class; do not use directly -class CThreadRep { -public: - CThreadRep(IJob*, void* userData); - - // manipulators - - // initialize the thread library - static void initThreads(); - - // change ref count - void ref(); - void unref(); - - // the calling thread sleeps for t seconds. if t == 0.0 then - // the thread yields the CPU. - void sleep(double timeout); - - // cancel the thread - void cancel(); - - // set cancellation state - bool enableCancel(bool enable); - - // permanently disable further cancellation and start cancel cleanup - // if cancel has been called and cancellation hasn't been started yet. - void testCancel(); - - // wait for thread to exit or for current thread to cancel - bool wait(CThreadRep*, double timeout); - -#if WINDOWS_LIKE - // wait for a message on the queue - bool waitForEvent(double timeout); -#endif - - // set the priority - void setPriority(int n); - - // accessors - - // get the exit result for this thread. thread must be terminated. - void* getResult() const; - - // get the user data passed to the constructor - void* getUserData() const; - - // get the current cancellable state - bool isCancellable() const; - -#if HAVE_PTHREAD - bool isExited() const; -#elif WINDOWS_LIKE - HANDLE getExitEvent() const; - HANDLE getCancelEvent() const; -#endif - - // return the thread rep for the calling thread. the returned - // rep has been ref()'d. - static CThreadRep* getCurrentThreadRep(); - -protected: - virtual ~CThreadRep(); - -private: - // internal constructor - CThreadRep(); - - // initialization/cleanup - void init(); - void fini(); - - // thread rep lookup - static CThreadRep* find(); - - // thread functions -#if HAVE_PTHREAD - static void* threadFunc(void* arg); - static void threadCancel(int); - static void* threadSignalHandler(void*); -#elif WINDOWS_LIKE - static unsigned int __stdcall threadFunc(void* arg); -#endif - void doThreadFunc(); - - // not implemented - CThreadRep(const CThreadRep&); - CThreadRep& operator=(const CThreadRep&); - -private: - static CMutex* s_mutex; - static CThreadRep* s_head; - - CThreadRep* m_prev; - CThreadRep* m_next; - - int m_refCount; - IJob* m_job; - void* m_userData; - void* m_result; - bool m_cancellable; - bool m_cancelling; - -#if HAVE_PTHREAD - pthread_t m_thread; - bool m_exit; - bool m_cancel; - static pthread_t s_signalThread; -#endif - -#if WINDOWS_LIKE - HANDLE m_thread; - DWORD m_id; - HANDLE m_exit; - HANDLE m_cancel; -#endif -}; - -// -// CThreadPtr -- auto unref'ing pointer to thread rep -// - -class CThreadPtr { -public: - CThreadPtr(CThreadRep* rep) : m_rep(rep) { } - ~CThreadPtr() { m_rep->unref(); } - - CThreadRep* operator->() const { return m_rep; } - -private: - // not implemented - CThreadPtr(const CThreadPtr&); - CThreadPtr& operator=(const CThreadPtr&); - -private: - CThreadRep* m_rep; -}; - -#endif diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp index 91355bc7..963b5799 100644 --- a/lib/mt/CTimerThread.cpp +++ b/lib/mt/CTimerThread.cpp @@ -16,6 +16,7 @@ #include "CThread.h" #include "TMethodJob.h" #include "CLog.h" +#include "CArch.h" // // CTimerThread @@ -50,7 +51,7 @@ void CTimerThread::timer(void*) { LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); - CThread::sleep(m_timeout); + ARCH->sleep(m_timeout); LOG((CLOG_DEBUG1 "timeout")); m_callingThread->cancel(); } diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index baf32d7e..6eda91d6 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -29,18 +29,18 @@ libmt_a_SOURCES = \ CMutex.cpp \ CCondVar.cpp \ CThread.cpp \ - CThreadRep.cpp \ CTimerThread.cpp \ XMT.cpp \ CCondVar.h \ CLock.h \ CMutex.h \ CThread.h \ - CThreadRep.h \ CTimerThread.h \ XMT.h \ XThread.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ $(NULL) diff --git a/lib/mt/XMT.h b/lib/mt/XMT.h index 32ca9aaa..f66428ea 100644 --- a/lib/mt/XMT.h +++ b/lib/mt/XMT.h @@ -18,16 +18,12 @@ #include "XBase.h" //! Generic multithreading exception -class XMT : public XBase { }; +XBASE_SUBCLASS(XMT, XBase); //! Thread creation exception /*! Thrown when a thread cannot be created. */ -class XMTThreadUnavailable : public XMT { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XMTThreadUnavailable, XMT); #endif diff --git a/lib/mt/XThread.h b/lib/mt/XThread.h index b76c9933..30dff1da 100644 --- a/lib/mt/XThread.h +++ b/lib/mt/XThread.h @@ -15,13 +15,7 @@ #ifndef XTHREAD_H #define XTHREAD_H -//! Generic thread exception -/*! -Exceptions derived from this class are used by the multithreading -library to perform stack unwinding when a thread terminates. These -exceptions must always be rethrown by clients when caught. -*/ -class XThread { }; +#include "XArch.h" //! Thread exception to exit /*! @@ -39,20 +33,4 @@ public: void* m_result; }; -//! Thread exception to cancel -/*! -Thrown to cancel a thread. Clients must not throw this type, but -must rethrow it if caught (by XThreadCancel, XThread, or ...). -*/ -class XThreadCancel : public XThread { }; - -/*! -\def RETHROW_XTHREAD -Convenience macro to rethrow an XThread exception but ignore other -exceptions. Put this in your catch (...) handler after necessary -cleanup but before leaving or returning from the handler. -*/ -#define RETHROW_XTHREAD \ - try { throw; } catch (XThread&) { throw; } catch (...) { } - #endif diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index 2cfdc7a1..f926fc1d 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -103,10 +103,6 @@ SOURCE=.\CThread.cpp # End Source File # Begin Source File -SOURCE=.\CThreadRep.cpp -# End Source File -# Begin Source File - SOURCE=.\CTimerThread.cpp # End Source File # Begin Source File @@ -135,10 +131,6 @@ SOURCE=.\CThread.h # End Source File # Begin Source File -SOURCE=.\CThreadRep.h -# End Source File -# Begin Source File - SOURCE=.\CTimerThread.h # End Source File # Begin Source File diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp deleted file mode 100644 index e473b933..00000000 --- a/lib/net/CNetwork.cpp +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CNetwork.h" -#include "XNetwork.h" -#include "CLog.h" -#include - -// -// CNetwork -// - -#if WINDOWS_LIKE - -static CNetwork::Socket (PASCAL FAR *accept_winsock)(CNetwork::Socket s, CNetwork::Address FAR *addr, CNetwork::AddressLength FAR *addrlen); -static int (PASCAL FAR *bind_winsock)(CNetwork::Socket s, const CNetwork::Address FAR *addr, CNetwork::AddressLength namelen); -static int (PASCAL FAR *close_winsock)(CNetwork::Socket s); -static int (PASCAL FAR *connect_winsock)(CNetwork::Socket s, const CNetwork::Address FAR *name, CNetwork::AddressLength namelen); -static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen); -static int (PASCAL FAR *getpeername_winsock)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); -static int (PASCAL FAR *getsockerror_winsock)(void); -static int (PASCAL FAR *getsockname_winsock)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); -static int (PASCAL FAR *getsockopt_winsock)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); -static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); -static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); -static int (PASCAL FAR *ioctl_winsock)(CNetwork::Socket s, int cmd, void FAR *); -static int (PASCAL FAR *listen_winsock)(CNetwork::Socket s, int backlog); -static ssize_t (PASCAL FAR *recv_winsock)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); -static ssize_t (PASCAL FAR *recvfrom_winsock)(CNetwork::Socket s, void FAR * buf, size_t len, int flags, CNetwork::Address FAR *from, CNetwork::AddressLength FAR * fromlen); -static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); -static ssize_t (PASCAL FAR *send_winsock)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags); -static ssize_t (PASCAL FAR *sendto_winsock)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags, const CNetwork::Address FAR *to, CNetwork::AddressLength tolen); -static int (PASCAL FAR *setsockopt_winsock)(CNetwork::Socket s, int level, int optname, const void FAR * optval, CNetwork::AddressLength optlen); -static int (PASCAL FAR *shutdown_winsock)(CNetwork::Socket s, int how); -static CNetwork::Socket (PASCAL FAR *socket_winsock)(int af, int type, int protocol); -static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); -static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); -static struct servent FAR * (PASCAL FAR *getservbyport_winsock)(int port, const char FAR * proto); -static struct servent FAR * (PASCAL FAR *getservbyname_winsock)(const char FAR * name, const char FAR * proto); -static struct protoent FAR * (PASCAL FAR *getprotobynumber_winsock)(int proto); -static struct protoent FAR * (PASCAL FAR *getprotobyname_winsock)(const char FAR * name); -static int (PASCAL FAR *WSACleanup_winsock)(void); -static int (PASCAL FAR *WSAFDIsSet_winsock)(CNetwork::Socket, fd_set FAR *); - -const int CNetwork::Error = SOCKET_ERROR; -const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; - -#undef FD_ISSET -#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) - -// have poll() pick up our select() pointer -#define select select_winsock - -static HMODULE s_networkModule = NULL; - -static -FARPROC -netGetProcAddress(HMODULE module, LPCSTR name) -{ - FARPROC func = ::GetProcAddress(module, name); - if (!func) { - throw XNetworkFunctionUnavailable(name); - } - return func; -} - -void -CNetwork::init() -{ - assert(WSACleanup_winsock == NULL); - assert(s_networkModule == NULL); - - // try winsock 2 - HMODULE module = (HMODULE)::LoadLibrary("ws2_32.dll"); - if (module == NULL) { - LOG((CLOG_NOTE "ws2_32.dll not found")); - } - else { - try { - init2(module); - return; - } - catch (XNetwork& e) { - LOG((CLOG_NOTE "ws2_32.dll error: %s", e.what())); - } - } - - // try winsock 1 - module = (HMODULE)::LoadLibrary("wsock32.dll"); - if (module == NULL) { - LOG((CLOG_NOTE "wsock32.dll not found")); - } - else { - try { - init2(module); - return; - } - catch (XNetwork& e) { - LOG((CLOG_NOTE "wsock32.dll error: %s", e.what())); - } - } - - // no networking - throw XNetworkUnavailable(); -} - -void -CNetwork::cleanup() -{ - if (s_networkModule != NULL) { - WSACleanup_winsock(); - ::FreeLibrary(s_networkModule); - - WSACleanup_winsock = NULL; - s_networkModule = NULL; - } -} - -UInt32 -CNetwork::swaphtonl(UInt32 v) -{ - static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; - if (s_endian.b[0] == 0x34) { - return ((v & 0xff000000lu) >> 24) | - ((v & 0x00ff0000lu) >> 8) | - ((v & 0x0000ff00lu) << 8) | - ((v & 0x000000fflu) << 24); - } - else { - return v; - } -} - -UInt16 -CNetwork::swaphtons(UInt16 v) -{ - static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; - if (s_endian.b[0] == 0x34) { - return static_cast( ((v & 0xff00u) >> 8) | - ((v & 0x00ffu) << 8)); - } - else { - return v; - } -} - -UInt32 -CNetwork::swapntohl(UInt32 v) -{ - return swaphtonl(v); -} - -UInt16 -CNetwork::swapntohs(UInt16 v) -{ - return swaphtons(v); -} - -#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) - -void -CNetwork::init2( - HMODULE module) -{ - assert(module != NULL); - - // get startup function address - int (PASCAL FAR *startup)(WORD, LPWSADATA); - setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); - - // startup network library - WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); - WSADATA data; - int err = startup(version, &data); - if (data.wVersion != version) { - throw XNetworkVersion(LOBYTE(data.wVersion), HIBYTE(data.wVersion)); - } - if (err != 0) { - throw XNetworkFailed(); - } - - // get function addresses - setfunc(accept_winsock, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); - setfunc(bind_winsock, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); - setfunc(close_winsock, closesocket, int (PASCAL FAR *)(Socket s)); - setfunc(connect_winsock, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); - setfunc(getpeername_winsock, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void)); - setfunc(getsockname_winsock, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); - setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); - setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); - setfunc(listen_winsock, listen, int (PASCAL FAR *)(Socket s, int backlog)); - setfunc(recv_winsock, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); - setfunc(recvfrom_winsock, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); - setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); - setfunc(send_winsock, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); - setfunc(sendto_winsock, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); - setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); - setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(Socket s, int how)); - setfunc(socket_winsock, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); - setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); - setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(getservbyport_winsock, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); - setfunc(getservbyname_winsock, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); - setfunc(getprotobynumber_winsock, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); - setfunc(getprotobyname_winsock, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); - setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); - - s_networkModule = module; -} - -CNetwork::Socket -CNetwork::accept(Socket s, Address* addr, AddressLength* addrlen) -{ - return accept_winsock(s, addr, addrlen); -} - -int -CNetwork::bind(Socket s, const Address* addr, AddressLength namelen) -{ - return bind_winsock(s, addr, namelen); -} - -int -CNetwork::close(Socket s) -{ - return close_winsock(s); -} - -int -CNetwork::connect(Socket s, const Address* name, AddressLength namelen) -{ - return connect_winsock(s, name, namelen); -} - -int -CNetwork::gethostname(char* name, int namelen) -{ - return gethostname_winsock(name, namelen); -} - -int -CNetwork::getpeername(Socket s, Address* name, AddressLength* namelen) -{ - return getpeername_winsock(s, name, namelen); -} - -int -CNetwork::getsockerror(void) -{ - return getsockerror_winsock(); -} - -int -CNetwork::getsockname(Socket s, Address* name, AddressLength* namelen) -{ - return getsockname_winsock(s, name, namelen); -} - -int -CNetwork::getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen) -{ - return getsockopt_winsock(s, level, optname, optval, optlen); -} - -int -CNetwork::inet_aton(const char* cp, InternetAddress* addr) -{ - assert(addr != NULL); - - // fake it with inet_addr, which is per-thread on winsock - unsigned long inetAddr = inet_addr_winsock(cp); - if (inetAddr == INADDR_NONE) { - return 0; - } - else { - addr->s_addr = inetAddr; - return 1; - } -} - -CString -CNetwork::inet_ntoa(struct in_addr in) -{ - // winsock returns strings per-thread - return CString(inet_ntoa_winsock(in)); -} - -int -CNetwork::ioctl(Socket s, int cmd, void* arg) -{ - return ioctl_winsock(s, cmd, arg); -} - -int -CNetwork::listen(Socket s, int backlog) -{ - return listen_winsock(s, backlog); -} - -ssize_t -CNetwork::read(Socket s, void* buf, size_t len) -{ - return recv_winsock(s, buf, len, 0); -} - -ssize_t -CNetwork::recv(Socket s, void* buf, size_t len, int flags) -{ - return recv_winsock(s, buf, len, flags); -} - -ssize_t -CNetwork::recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen) -{ - return recvfrom_winsock(s, buf, len, flags, from, fromlen); -} - -ssize_t -CNetwork::send(Socket s, const void* buf, size_t len, int flags) -{ - return send_winsock(s, buf, len, flags); -} - -ssize_t -CNetwork::sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen) -{ - return sendto_winsock(s, buf, len, flags, to, tolen); -} - -int -CNetwork::setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen) -{ - return setsockopt_winsock(s, level, optname, optval, optlen); -} - -int -CNetwork::shutdown(Socket s, int how) -{ - return shutdown_winsock(s, how); -} - -CNetwork::Socket -CNetwork::socket(int af, int type, int protocol) -{ - return socket_winsock(af, type, protocol); -} - -ssize_t -CNetwork::write(Socket s, const void* buf, size_t len) -{ - return send_winsock(s, buf, len, 0); -} - -int -CNetwork::gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type) -{ - assert(hostinfo != NULL); - - // winsock returns structures per-thread - struct hostent FAR* info = gethostbyaddr_winsock(addr, len, type); - if (info == NULL) { - return getsockerror(); - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::gethostbyname(CHostInfo* hostinfo, const char* name) -{ - assert(hostinfo != NULL); - - // winsock returns structures per-thread - struct hostent FAR* info = gethostbyname_winsock(name); - if (info == NULL) { - return getsockerror(); - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyport(CServiceInfo* servinfo, int port, const char* proto) -{ - assert(servinfo != NULL); - - // winsock returns structures per-thread - struct servent FAR* info = getservbyport_winsock(port, proto); - if (info == NULL) { - return getsockerror(); - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyname(CServiceInfo* servinfo, const char* name, const char* proto) -{ - assert(servinfo != NULL); - - // winsock returns structures per-thread - struct servent FAR* info = getservbyname_winsock(name, proto); - if (info == NULL) { - return getsockerror(); - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobynumber(CProtocolInfo* protoinfo, int proto) -{ - assert(protoinfo != NULL); - - // winsock returns structures per-thread - struct protoent FAR* info = getprotobynumber_winsock(proto); - if (info == NULL) { - return getsockerror(); - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobyname(CProtocolInfo* protoinfo, const char* name) -{ - assert(protoinfo != NULL); - - // winsock returns structures per-thread - struct protoent FAR* info = getprotobyname_winsock(name); - if (info == NULL) { - return getsockerror(); - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::setblocking(Socket s, bool blocking) -{ - int flag = blocking ? 0 : 1; - return ioctl(s, FIONBIO, &flag); -} - -int -CNetwork::setnodelay(Socket s, bool nodelay) -{ - BOOL flag = nodelay ? 1 : 0; - return setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); -} - -#endif - -#if UNIX_LIKE - -#include "CMutex.h" -#include "CLock.h" - -#if HAVE_UNISTD_H -# include -#endif -#include -#include -#include -#if !defined(TCP_NODELAY) -# include -#endif - -const int CNetwork::Error = -1; -const CNetwork::Socket CNetwork::Null = -1; - -static CMutex* s_networkMutex = NULL; - -#define setfunc(var, name, type) var = (type)::name - -UInt32 -CNetwork::swaphtonl(UInt32 v) -{ - return htonl(v); -} - -UInt16 -CNetwork::swaphtons(UInt16 v) -{ - return htons(v); -} - -UInt32 -CNetwork::swapntohl(UInt32 v) -{ - return ntohl(v); -} - -UInt16 -CNetwork::swapntohs(UInt16 v) -{ - return ntohs(v); -} - -void -CNetwork::init() -{ - assert(s_networkMutex == NULL); - - try { - s_networkMutex = new CMutex; - } - catch (...) { - throw XNetworkFailed(); - } -} - -void -CNetwork::cleanup() -{ - delete s_networkMutex; - s_networkMutex = NULL; -} - -CNetwork::Socket -CNetwork::accept(Socket s, Address* addr, AddressLength* addrlen) -{ - return ::accept(s, addr, addrlen); -} - -int -CNetwork::bind(Socket s, const Address* addr, AddressLength namelen) -{ - return ::bind(s, addr, namelen); -} - -int -CNetwork::close(Socket s) -{ - return ::close(s); -} - -int -CNetwork::connect(Socket s, const Address* name, AddressLength namelen) -{ - return ::connect(s, name, namelen); -} - -int -CNetwork::gethostname(char* name, int namelen) -{ - return ::gethostname(name, namelen); -} - -int -CNetwork::getpeername(Socket s, Address* name, AddressLength* namelen) -{ - return ::getpeername(s, name, namelen); -} - -int -CNetwork::getsockerror(void) -{ - return errno; -} - -int -CNetwork::getsockname(Socket s, Address* name, AddressLength* namelen) -{ - return ::getsockname(s, name, namelen); -} - -int -CNetwork::getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen) -{ - return ::getsockopt(s, level, optname, optval, optlen); -} - -int -CNetwork::inet_aton(const char* cp, InternetAddress* addr) -{ - return ::inet_aton(cp, addr); -} - -CString -CNetwork::inet_ntoa(struct in_addr in) -{ - // single threaded access to inet_ntoa functions - CLock lock(s_networkMutex); - return CString(::inet_ntoa(in)); -} - -int -CNetwork::ioctl(Socket s, int cmd, void* arg) -{ - return ::ioctl(s, cmd, arg); -} - -int -CNetwork::listen(Socket s, int backlog) -{ - return ::listen(s, backlog); -} - -ssize_t -CNetwork::read(Socket s, void* buf, size_t len) -{ - return ::read(s, buf, len); -} - -ssize_t -CNetwork::recv(Socket s, void* buf, size_t len, int flags) -{ - return ::recv(s, buf, len, flags); -} - -ssize_t -CNetwork::recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen) -{ - return ::recvfrom(s, buf, len, flags, from, fromlen); -} - -ssize_t -CNetwork::send(Socket s, const void* buf, size_t len, int flags) -{ - return ::send(s, buf, len, flags); -} - -ssize_t -CNetwork::sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen) -{ - return ::sendto(s, buf, len, flags, to, tolen); -} - -int -CNetwork::setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen) -{ - return ::setsockopt(s, level, optname, optval, optlen); -} - -int -CNetwork::shutdown(Socket s, int how) -{ - return ::shutdown(s, how); -} - -CNetwork::Socket -CNetwork::socket(int af, int type, int protocol) -{ - return ::socket(af, type, protocol); -} - -ssize_t -CNetwork::write(Socket s, const void* buf, size_t len) -{ - return ::write(s, buf, len); -} - -int -CNetwork::gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type) -{ - assert(hostinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct hostent* info = ::gethostbyaddr(addr, len, type); - if (info == NULL) { - return h_errno; - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::gethostbyname(CHostInfo* hostinfo, const char* name) -{ - assert(hostinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct hostent* info = ::gethostbyname(name); - if (info == NULL) { - return h_errno; - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyport(CServiceInfo* servinfo, int port, const char* proto) -{ - assert(servinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct servent* info = ::getservbyport(port, proto); - if (info == NULL) { - return -1; - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyname(CServiceInfo* servinfo, const char* name, const char* proto) -{ - assert(servinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct servent* info = ::getservbyname(name, proto); - if (info == NULL) { - return -1; - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobynumber(CProtocolInfo* protoinfo, int proto) -{ - assert(protoinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct protoent* info = ::getprotobynumber(proto); - if (info == NULL) { - return -1; - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobyname(CProtocolInfo* protoinfo, const char* name) -{ - assert(protoinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct protoent* info = ::getprotobyname(name); - if (info == NULL) { - return -1; - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::setblocking(CNetwork::Socket s, bool blocking) -{ - int mode = fcntl(s, F_GETFL, 0); - if (mode == -1) { - return -1; - } - if (blocking) { - mode &= ~O_NDELAY; - } - else { - mode |= O_NDELAY; - } - if (fcntl(s, F_SETFL, mode) < 0) { - return -1; - } - return 0; -} - -int -CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) -{ - int flag = nodelay ? 1 : 0; - return setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); -} - -#endif - -#if HAVE_POLL - -int -CNetwork::poll(PollEntry fd[], int nfds, int timeout) -{ - return ::poll(fd, nfds, timeout); -} - -#else // !HAVE_POLL - -#if HAVE_SYS_SELECT_H && !WINDOWS_LIKE -# include -#endif -#if HAVE_SYS_TIME_H -# include -#endif -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_UNISTD_H -# include -#endif - -int -CNetwork::poll(PollEntry fd[], int nfds, int timeout) -{ - int i; - - // prepare sets for select - int n = 0; - fd_set readSet, writeSet, errSet; - fd_set* readSetP = NULL; - fd_set* writeSetP = NULL; - fd_set* errSetP = NULL; - FD_ZERO(&readSet); - FD_ZERO(&writeSet); - FD_ZERO(&errSet); - for (i = 0; i < nfds; ++i) { - int fdi = static_cast(fd[i].fd); - if (fd[i].events & kPOLLIN) { - FD_SET(fd[i].fd, &readSet); - readSetP = &readSet; - if (fdi > n) { - n = fdi; - } - } - if (fd[i].events & kPOLLOUT) { - FD_SET(fd[i].fd, &writeSet); - writeSetP = &writeSet; - if (fdi > n) { - n = fdi; - } - } - if (true) { - FD_SET(fd[i].fd, &errSet); - errSetP = &errSet; - if (fdi > n) { - n = fdi; - } - } - } - - // prepare timeout for select - struct timeval timeout2; - struct timeval* timeout2P; - if (timeout < 0) { - timeout2P = NULL; - } - else { - timeout2P = &timeout2; - timeout2.tv_sec = timeout / 1000; - timeout2.tv_usec = 1000 * (timeout % 1000); - } - - // do the select. note that winsock ignores the first argument. - n = select((SELECT_TYPE_ARG1) n + 1, - SELECT_TYPE_ARG234 readSetP, - SELECT_TYPE_ARG234 writeSetP, - SELECT_TYPE_ARG234 errSetP, - SELECT_TYPE_ARG5 timeout2P); - - // handle results - if (n == Error) { - return Error; - } - if (n == 0) { - return 0; - } - n = 0; - for (i = 0; i < nfds; ++i) { - fd[i].revents = 0; - if (FD_ISSET(fd[i].fd, &readSet)) { - fd[i].revents |= kPOLLIN; - } - if (FD_ISSET(fd[i].fd, &writeSet)) { - fd[i].revents |= kPOLLOUT; - } - if (FD_ISSET(fd[i].fd, &errSet)) { - fd[i].revents |= kPOLLERR; - } - if (fd[i].revents != 0) { - ++n; - } - } - return n; -} - -#endif // !HAVE_POLL - - -// -// CNetwork::CHostInfo -// - -CNetwork::CHostInfo::CHostInfo(const struct hostent* hent) -{ - assert(hent != NULL); - - m_name = hent->h_name; - m_addressType = hent->h_addrtype; - m_addressLength = hent->h_length; - for (char** scan = hent->h_aliases; *scan != NULL; ++scan) { - m_aliases.push_back(*scan); - } - - // concatenate addresses together - UInt32 n = 0; - for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { - ++n; - } - m_addressData.reserve(n); - for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { - m_addressData.append(*scan, m_addressLength); - } - - // set pointers into concatenated data - const char* data = m_addressData.data(); - for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { - m_addresses.push_back(data); - data += m_addressLength; - } -} - -void -CNetwork::CHostInfo::swap(CHostInfo& v) -{ - std::swap(m_name, v.m_name); - std::swap(m_aliases, v.m_aliases); - std::swap(m_addressType, v.m_addressType); - std::swap(m_addressLength, v.m_addressLength); - std::swap(m_addresses, v.m_addresses); - std::swap(m_addressData, v.m_addressData); -} - - -// -// CNetwork::CServiceInfo -// - -CNetwork::CServiceInfo::CServiceInfo(const struct servent* sent) -{ - assert(sent != NULL); - - m_name = sent->s_name; - m_port = sent->s_port; - m_protocol = sent->s_proto; - for (char** scan = sent->s_aliases; *scan != NULL; ++scan) { - m_aliases.push_back(*scan); - } -} - -void -CNetwork::CServiceInfo::swap(CServiceInfo& v) -{ - std::swap(m_name, v.m_name); - std::swap(m_aliases, v.m_aliases); - std::swap(m_port, v.m_port); - std::swap(m_protocol, v.m_protocol); -} - - -// -// CNetwork::CProtocolInfo -// - -CNetwork::CProtocolInfo::CProtocolInfo(const struct protoent* pent) -{ - assert(pent != NULL); - - m_name = pent->p_name; - m_protocol = pent->p_proto; - for (char** scan = pent->p_aliases; *scan != NULL; ++scan) { - m_aliases.push_back(*scan); - } -} - -void -CNetwork::CProtocolInfo::swap(CProtocolInfo& v) -{ - std::swap(m_name, v.m_name); - std::swap(m_aliases, v.m_aliases); - std::swap(m_protocol, v.m_protocol); -} diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h deleted file mode 100644 index 80873a55..00000000 --- a/lib/net/CNetwork.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CNETWORK_H -#define CNETWORK_H - -#include "BasicTypes.h" -#include "CString.h" -#include "stdvector.h" - -#if !defined(HAVE_SOCKLEN_T) -// Darwin is so unsure what to use for socklen_t it makes us choose -# if defined(__APPLE__) -# if !defined(_BSD_SOCKLEN_T_) -# define _BSD_SOCKLEN_T_ int -# endif -# else -typedef int socklen_t; -# endif -#endif - -#if WINDOWS_LIKE - // declare no functions in winsock2 -# define INCL_WINSOCK_API_PROTOTYPES 0 -# define INCL_WINSOCK_API_TYPEDEFS 0 -# include -typedef int ssize_t; -# define SELECT_TYPE_ARG1 int -# define SELECT_TYPE_ARG234 (fd_set *) -# define SELECT_TYPE_ARG5 (struct timeval *) -#endif - -#if UNIX_LIKE -# if HAVE_SYS_TYPES_H -# include -# endif -# if HAVE_SYS_SOCKET_H -# include -# endif -# if HAVE_POLL -# include -# endif -# include -# include -# include -#endif - -//! Networking functions -class CNetwork { -public: - // platform dependent types -#if WINDOWS_LIKE - typedef SOCKET Socket; - typedef struct sockaddr Address; - typedef int AddressLength; - typedef short AddressType; - typedef struct in_addr InternetAddress; -#elif UNIX_LIKE - typedef int Socket; - typedef struct sockaddr Address; - typedef socklen_t AddressLength; - typedef int AddressType; - typedef struct in_addr InternetAddress; -#endif - -#if !HAVE_POLL - class PollEntry { - public: - Socket fd; - short events; - short revents; - }; - enum { - kPOLLIN = 1, - kPOLLOUT = 2, - kPOLLERR = 4, - kPOLLNVAL = 8 - }; -#else - typedef struct pollfd PollEntry; - enum { - kPOLLIN = POLLIN, - kPOLLOUT = POLLOUT, - kPOLLERR = POLLERR, - kPOLLNVAL = POLLNVAL - }; -#endif - - //! Host name information - class CHostInfo { - public: - CHostInfo() { } - CHostInfo(const struct hostent*); - - void swap(CHostInfo&); - - public: - typedef std::vector AliasList; - typedef std::vector AddressList; - - CString m_name; - AliasList m_aliases; - AddressType m_addressType; - int m_addressLength; - AddressList m_addresses; - - private: - std::string m_addressData; - }; - - //! Network service information - class CServiceInfo { - public: - CServiceInfo() { } - CServiceInfo(const struct servent*); - - void swap(CServiceInfo&); - - public: - typedef std::vector AliasList; - - CString m_name; - AliasList m_aliases; - int m_port; - CString m_protocol; - }; - - //! Network protocol information - class CProtocolInfo { - public: - CProtocolInfo() { } - CProtocolInfo(const struct protoent*); - - void swap(CProtocolInfo&); - - public: - typedef std::vector AliasList; - - CString m_name; - AliasList m_aliases; - int m_protocol; - }; - - //! @name manipulators - //@{ - - //! Initialize network subsystem - /*! - This \b must be called before any other calls to the network subsystem. - */ - static void init(); - - //! Clean up network subsystem - /*! - This should be called when the network subsystem is no longer needed - and no longer in use. - */ - static void cleanup(); - - // byte swapping functions - //! Swap bytes to network order - static UInt32 swaphtonl(UInt32 hostlong); - //! Swap bytes to network order - static UInt16 swaphtons(UInt16 hostshort); - //! Swap bytes to host order - static UInt32 swapntohl(UInt32 netlong); - //! Swap bytes to host order - static UInt16 swapntohs(UInt16 netshort); - - //@} - //! @name constants - //@{ - - //! The error type - static const int Error; - //! The non-socket - static const Socket Null; - - // getsockerror() constants - enum { -#if WINDOWS_LIKE - kEADDRINUSE = WSAEADDRINUSE, - kECONNECTING = WSAEWOULDBLOCK, - kEINTR = WSAEINTR, -#elif UNIX_LIKE - kEADDRINUSE = EADDRINUSE, - kECONNECTING = EINPROGRESS, - kEINTR = EINTR, -#endif - kNone = 0 - }; - - // gethosterror() constants - enum { -#if WINDOWS_LIKE - kHOST_NOT_FOUND = WSAHOST_NOT_FOUND, - kNO_DATA = WSANO_DATA, - kNO_RECOVERY = WSANO_RECOVERY, - kTRY_AGAIN = WSATRY_AGAIN, -#elif UNIX_LIKE - kHOST_NOT_FOUND = HOST_NOT_FOUND, - kNO_DATA = NO_DATA, - kNO_RECOVERY = NO_RECOVERY, - kTRY_AGAIN = TRY_AGAIN, -#endif - kHOST_OK = 0 - }; - - //@} - - // socket interface (only available after init()) - - static Socket accept(Socket s, Address* addr, AddressLength* addrlen); - static int bind(Socket s, const Address* addr, AddressLength namelen); - static int close(Socket s); - static int connect(Socket s, const Address* name, AddressLength namelen); - static int gethostname(char* name, int namelen); - static int getpeername(Socket s, Address* name, AddressLength* namelen); - static int getsockerror(void); - static int getsockname(Socket s, Address* name, AddressLength* namelen); - static int getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen); - static int inet_aton(const char* cp, InternetAddress* addr); - static CString inet_ntoa(struct in_addr in); - static int ioctl(Socket s, int cmd, void* ); - static int listen(Socket s, int backlog); - static int poll(PollEntry[], int nfds, int timeout); - static ssize_t read(Socket s, void* buf, size_t len); - static ssize_t recv(Socket s, void* buf, size_t len, int flags); - static ssize_t recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen); - static ssize_t send(Socket s, const void* buf, size_t len, int flags); - static ssize_t sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen); - static int setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen); - static int shutdown(Socket s, int how); - static Socket socket(int af, int type, int protocol); - static ssize_t write(Socket s, const void* buf, size_t len); - static int gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type); - static int gethostbyname(CHostInfo* hostinfo, const char* name); - static int getservbyport(CServiceInfo* servinfo, int port, const char* proto); - static int getservbyname(CServiceInfo* servinfo, const char* name, const char* proto); - static int getprotobynumber(CProtocolInfo* protoinfo, int proto); - static int getprotobyname(CProtocolInfo* protoinfo, const char* name); - - // convenience functions (only available after init()) - - //! Set socket to (non-)blocking operation - static int setblocking(Socket s, bool blocking); - - //! Turn Nagle algorithm on or off on socket - /*! - Set socket to send messages immediately (true) or to collect small - messages into one packet (false). - */ - static int setnodelay(Socket s, bool nodelay); - -private: -#if WINDOWS_LIKE - static void init2(HMODULE); -#endif -}; - -#endif diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index d626b31d..c60b96a5 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -14,53 +14,47 @@ #include "CNetworkAddress.h" #include "XSocket.h" +#include "CArch.h" +#include "XArch.h" #include -#include // // CNetworkAddress // CNetworkAddress::CNetworkAddress() : - m_port(0) + m_address(NULL) { // note -- make no calls to CNetwork socket interface here; // we're often called prior to CNetwork::init(). - - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(m_port); - inetAddress->sin_addr.s_addr = INADDR_ANY; - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } -CNetworkAddress::CNetworkAddress(UInt16 port) : - m_port(port) +CNetworkAddress::CNetworkAddress(int port) { if (port == 0) { - throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); + throw XSocketAddress(XSocketAddress::kBadPort, "", port); } - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(m_port); - inetAddress->sin_addr.s_addr = INADDR_ANY; - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); } -CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : - m_hostname(hostname_), - m_port(port) +CNetworkAddress::CNetworkAddress(const CNetworkAddress& addr) : + m_address(ARCH->copyAddr(addr.m_address)), + m_hostname(addr.m_hostname) { - CString hostname(m_hostname); + // do nothing +} +CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : + m_hostname(hostname_) +{ if (port == 0) { - throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); + throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, port); } // check for port suffix + CString hostname(m_hostname); CString::size_type i = hostname.rfind(':'); if (i != CString::npos && i + 1 < hostname.size()) { // found a colon. see if it looks like an IPv6 address. @@ -92,81 +86,77 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : suffixPort <= 0 || suffixPort > 65535) { // bogus port throw XSocketAddress(XSocketAddress::kBadPort, - m_hostname, m_port); + m_hostname, port); } else { // good port - port = static_cast(suffixPort); + port = static_cast(suffixPort); hostname.erase(i); } } } // if hostname is empty then use wildcard address - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); if (hostname.empty()) { - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(port); - inetAddress->sin_addr.s_addr = INADDR_ANY; - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); - return; + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); } - - // convert dot notation to address - if (CNetwork::inet_aton(hostname.c_str(), &inetAddress->sin_addr) != 0) { - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(port); - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); - return; + else { + // look up name + try { + m_address = ARCH->nameToAddr(hostname); + ARCH->setAddrPort(m_address, port); + } + catch (XArchNetworkNameUnknown&) { + throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); + } + catch (XArchNetworkNameNoAddress&) { + throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); + } + catch (XArchNetworkName&) { + throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); + } } - - // look up name - CNetwork::CHostInfo hostInfo; - switch (CNetwork::gethostbyname(&hostInfo, hostname.c_str())) { - case CNetwork::kHOST_OK: - break; - - case CNetwork::kHOST_NOT_FOUND: - throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); - - case CNetwork::kNO_DATA: - throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); - - case CNetwork::kNO_RECOVERY: - case CNetwork::kTRY_AGAIN: - default: - throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); - } - - inetAddress->sin_family = hostInfo.m_addressType; - inetAddress->sin_port = CNetwork::swaphtons(port); - memcpy(&inetAddress->sin_addr, hostInfo.m_addresses[0], - hostInfo.m_addressLength); - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } CNetworkAddress::~CNetworkAddress() { - // do nothing + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } +} + +CNetworkAddress& +CNetworkAddress::operator=(const CNetworkAddress& addr) +{ + CArchNetAddress newAddr = NULL; + if (addr.m_address != NULL) { + newAddr = ARCH->copyAddr(addr.m_address); + } + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } + m_hostname = addr.m_hostname; + m_address = newAddr; + return *this; } bool CNetworkAddress::isValid() const { - return (m_port != 0); + return (m_address != NULL); } -const CNetwork::Address* +const CArchNetAddress& CNetworkAddress::getAddress() const { - return &m_address; + return m_address; } - -CNetwork::AddressLength -CNetworkAddress::getAddressLength() const + +int +CNetworkAddress::getPort() const { - return sizeof(m_address); + return (m_address == NULL) ? 0 : ARCH->getAddrPort(m_address); } CString @@ -174,9 +164,3 @@ CNetworkAddress::getHostname() const { return m_hostname; } - -UInt16 -CNetworkAddress::getPort() const -{ - return m_port; -} diff --git a/lib/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h index fe68dcf6..02898278 100644 --- a/lib/net/CNetworkAddress.h +++ b/lib/net/CNetworkAddress.h @@ -15,9 +15,9 @@ #ifndef CNETWORKADDRESS_H #define CNETWORKADDRESS_H -#include "CNetwork.h" #include "CString.h" #include "BasicTypes.h" +#include "IArchNetwork.h" //! Network address type /*! @@ -34,7 +34,7 @@ public: Construct the wildcard address with the given port. \c port must not be zero. */ - CNetworkAddress(UInt16 port); + CNetworkAddress(int port); /*! Construct the network address for the given \c hostname and \c port. @@ -44,10 +44,14 @@ public: that suffix is extracted and used as the port, overridding the port parameter. Neither the extracted port or \c port may be zero. */ - CNetworkAddress(const CString& hostname, UInt16 port); + CNetworkAddress(const CString& hostname, int port); + + CNetworkAddress(const CNetworkAddress&); ~CNetworkAddress(); + CNetworkAddress& operator=(const CNetworkAddress&); + //! @name accessors //@{ @@ -62,14 +66,14 @@ public: Returns the address in the platform's native network address structure. */ - const CNetwork::Address* getAddress() const; + const CArchNetAddress& getAddress() const; - //! Get address length + //! Get port /*! - Returns the length of the address in the platform's native network - address structure. + Returns the port passed to the c'tor as a suffix to the hostname, + if that existed, otherwise as passed directly to the c'tor. */ - CNetwork::AddressLength getAddressLength() const; + int getPort() const; //! Get hostname /*! @@ -77,19 +81,11 @@ public: */ CString getHostname() const; - //! Get port - /*! - Returns the port passed to the c'tor as a suffix to the hostname, - if that existed, otherwise as passed directly to the c'tor. - */ - UInt16 getPort() const; - //@} private: - CNetwork::Address m_address; + CArchNetAddress m_address; CString m_hostname; - UInt16 m_port; }; #endif diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 83278166..1177516e 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -18,6 +18,8 @@ #include "XIO.h" #include "XSocket.h" #include "CThread.h" +#include "CArch.h" +#include "XArch.h" // // CTCPListenSocket @@ -25,16 +27,18 @@ CTCPListenSocket::CTCPListenSocket() { - m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == CNetwork::Null) { - throw XSocketCreate(); + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); } } CTCPListenSocket::~CTCPListenSocket() { try { - close(); + ARCH->closeSocket(m_socket); } catch (...) { // ignore @@ -44,15 +48,15 @@ CTCPListenSocket::~CTCPListenSocket() void CTCPListenSocket::bind(const CNetworkAddress& addr) { - if (CNetwork::bind(m_fd, addr.getAddress(), - addr.getAddressLength()) == CNetwork::Error) { - if (CNetwork::getsockerror() == CNetwork::kEADDRINUSE) { - throw XSocketAddressInUse(); - } - throw XSocketBind(); + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + ARCH->listenOnSocket(m_socket); } - if (CNetwork::listen(m_fd, 3) == CNetwork::Error) { - throw XSocketBind(); + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); } } @@ -60,31 +64,35 @@ IDataSocket* CTCPListenSocket::accept() { // accept asynchronously so we can check for cancellation - CNetwork::PollEntry pfds[1]; - pfds[0].fd = m_fd; - pfds[0].events = CNetwork::kPOLLIN; + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLIN; for (;;) { - CThread::testCancel(); - const int status = CNetwork::poll(pfds, 1, 10); - if (status > 0 && (pfds[0].revents & CNetwork::kPOLLIN) != 0) { - CNetwork::Address addr; - CNetwork::AddressLength addrlen = sizeof(addr); - CNetwork::Socket fd = CNetwork::accept(m_fd, &addr, &addrlen); - if (fd != CNetwork::Null) { - return new CTCPSocket(fd); + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0 && + (pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) { + return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); } } + catch (XArchNetwork&) { + // ignore and retry + } } } void CTCPListenSocket::close() { - if (m_fd == CNetwork::Null) { + if (m_socket == NULL) { throw XIOClosed(); } - if (CNetwork::close(m_fd) == CNetwork::Error) { - throw XSocketIOClose(); + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); } - m_fd = CNetwork::Null; } diff --git a/lib/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h index 15460f30..d5116379 100644 --- a/lib/net/CTCPListenSocket.h +++ b/lib/net/CTCPListenSocket.h @@ -16,7 +16,7 @@ #define CTCPLISTENSOCKET_H #include "IListenSocket.h" -#include "CNetwork.h" +#include "IArchNetwork.h" //! TCP listen socket /*! @@ -35,7 +35,7 @@ public: virtual IDataSocket* accept(); private: - CNetwork::Socket m_fd; + CArchSocket m_socket; }; #endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 0832904f..e74fc4f7 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -18,12 +18,12 @@ #include "CNetworkAddress.h" #include "XIO.h" #include "XSocket.h" -#include "CCondVar.h" #include "CLock.h" #include "CMutex.h" #include "CThread.h" -#include "CStopwatch.h" #include "TMethodJob.h" +#include "CArch.h" +#include "XArch.h" // // CTCPSocket @@ -31,17 +31,19 @@ CTCPSocket::CTCPSocket() { - m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == CNetwork::Null) { - throw XSocketCreate(); + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); } init(); } -CTCPSocket::CTCPSocket(CNetwork::Socket fd) : - m_fd(fd) +CTCPSocket::CTCPSocket(CArchSocket socket) : + m_socket(socket) { - assert(m_fd != CNetwork::Null); + assert(m_socket != NULL); init(); @@ -59,7 +61,7 @@ CTCPSocket::~CTCPSocket() close(); } catch (...) { - // ignore failures + // ignore } // clean up @@ -71,12 +73,14 @@ CTCPSocket::~CTCPSocket() void CTCPSocket::bind(const CNetworkAddress& addr) { - if (CNetwork::bind(m_fd, addr.getAddress(), - addr.getAddressLength()) == CNetwork::Error) { - if (errno == CNetwork::kEADDRINUSE) { - throw XSocketAddressInUse(); - } - throw XSocketBind(); + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + } + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); } } @@ -96,12 +100,21 @@ CTCPSocket::close() } // cause ioThread to exit - { + if (m_socket != NULL) { CLock lock(m_mutex); - if (m_fd != CNetwork::Null) { - CNetwork::shutdown(m_fd, 2); - m_connected = kClosed; + try { + ARCH->closeSocketForRead(m_socket); } + catch (XArchNetwork&) { + // ignore + } + try { + ARCH->closeSocketForWrite(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + m_connected = kClosed; } // wait for thread @@ -112,67 +125,71 @@ CTCPSocket::close() } // close socket - if (m_fd != CNetwork::Null) { - if (CNetwork::close(m_fd) == CNetwork::Error) { - throw XSocketIOClose(); + if (m_socket != NULL) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); } - m_fd = CNetwork::Null; } } void CTCPSocket::connect(const CNetworkAddress& addr) { - // connect asynchronously so we can check for cancellation - CNetwork::setblocking(m_fd, false); - if (CNetwork::connect(m_fd, addr.getAddress(), - addr.getAddressLength()) == CNetwork::Error) { - // check for failure - if (CNetwork::getsockerror() != CNetwork::kECONNECTING) { - XSocketConnect e; - CNetwork::setblocking(m_fd, true); - throw e; + do { + // connect asynchronously so we can check for cancellation. + // we can't wrap setting and resetting the blocking flag in + // the c'tor/d'tor of a class (to make resetting automatic) + // because setBlockingOnSocket() can throw and it might be + // called while unwinding the stack due to a throw. + try { + ARCH->setBlockingOnSocket(m_socket, false); + ARCH->connectSocket(m_socket, addr.getAddress()); + ARCH->setBlockingOnSocket(m_socket, true); + + // connected + break; + } + catch (XArchNetworkConnecting&) { + // connection is in progress + ARCH->setBlockingOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + ARCH->setBlockingOnSocket(m_socket, true); + throw XSocketConnect(e.what()); } // wait for connection or failure - CNetwork::PollEntry pfds[1]; - pfds[0].fd = m_fd; - pfds[0].events = CNetwork::kPOLLOUT; + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLOUT; for (;;) { - CThread::testCancel(); - const int status = CNetwork::poll(pfds, 1, 10); - if (status > 0) { - if ((pfds[0].revents & (CNetwork::kPOLLERR | - CNetwork::kPOLLNVAL)) != 0) { - // connection failed - int error = 0; - CNetwork::AddressLength size = sizeof(error); - CNetwork::setblocking(m_fd, true); - CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), &size); - throw XSocketConnect(error); - } - if ((pfds[0].revents & CNetwork::kPOLLOUT) != 0) { - int error; - CNetwork::AddressLength size = sizeof(error); - if (CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), - &size) == CNetwork::Error || - error != 0) { + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { // connection failed - CNetwork::setblocking(m_fd, true); - throw XSocketConnect(error); + ARCH->throwErrorOnSocket(m_socket); } + if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) { + // connection may have failed or succeeded + ARCH->throwErrorOnSocket(m_socket); - // connected! - break; + // connected! + break; + } } } + catch (XArchNetwork& e) { + throw XSocketConnect(e.what()); + } } - } - - // back to blocking - CNetwork::setblocking(m_fd, true); + } while (false); // start servicing the socket m_connected = kReadWrite; @@ -208,7 +225,20 @@ CTCPSocket::init() // turn off Nagle algorithm. we send lots of very short messages // that should be sent without (much) delay. for example, the // mouse motion messages are much less useful if they're delayed. - CNetwork::setnodelay(m_fd, true); +// FIXME -- the client should do this + try { + ARCH->setNoDelayOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork&) { + // ignore + } + throw XSocketCreate(e.what()); + } } void @@ -244,85 +274,81 @@ CTCPSocket::ioCleanup() void CTCPSocket::ioService() { - assert(m_fd != CNetwork::Null); + assert(m_socket != NULL); // now service the connection - CNetwork::PollEntry pfds[1]; - pfds[0].fd = m_fd; + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; for (;;) { { // choose events to poll for CLock lock(m_mutex); - pfds[0].events = 0; + pfds[0].m_events = 0; if (m_connected == 0) { return; } if ((m_connected & kRead) != 0) { // still open for reading - pfds[0].events |= CNetwork::kPOLLIN; + pfds[0].m_events |= IArchNetwork::kPOLLIN; } if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { // data queued for writing - pfds[0].events |= CNetwork::kPOLLOUT; + pfds[0].m_events |= IArchNetwork::kPOLLOUT; } } - // check for status - const int status = CNetwork::poll(pfds, 1, 10); + try { + // check for status + const int status = ARCH->pollSocket(pfds, 1, 0.01); - // transfer data and handle errors - if (status == 1) { - if ((pfds[0].revents & (CNetwork::kPOLLERR | - CNetwork::kPOLLNVAL)) != 0) { - // stream is no good anymore so bail - m_input->hangup(); - return; - } - - // read some data - if (pfds[0].revents & CNetwork::kPOLLIN) { - UInt8 buffer[4096]; - ssize_t n = CNetwork::read(m_fd, buffer, sizeof(buffer)); - if (n > 0) { + // transfer data and handle errors + if (status == 1) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { + // stream is no good anymore so bail CLock lock(m_mutex); - m_input->write(buffer, n); - } - else if (n == 0) { - // stream hungup m_input->hangup(); - m_connected &= ~kRead; + return; } - else { - // socket failed - if (CNetwork::getsockerror() != CNetwork::kEINTR) { - return; - } - } - } - - // write some data - if (pfds[0].revents & CNetwork::kPOLLOUT) { - CLock lock(m_mutex); - - // get amount of data to write - UInt32 n = m_output->getSize(); - - // write data - const void* buffer = m_output->peek(n); - ssize_t n2 = (UInt32)CNetwork::write(m_fd, buffer, n); - - // discard written data - if (n2 > 0) { - m_output->pop(n); - } - else if (n2 < 0) { - // socket failed - if (CNetwork::getsockerror() != CNetwork::kEINTR) { - return; + + // read some data + if (pfds[0].m_revents & IArchNetwork::kPOLLIN) { + UInt8 buffer[4096]; + size_t n = ARCH->readSocket(m_socket, + buffer, sizeof(buffer)); + CLock lock(m_mutex); + if (n > 0) { + m_input->write(buffer, n); + } + else { + // stream hungup + m_input->hangup(); + m_connected &= ~kRead; + } + } + + // write some data + if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) { + CLock lock(m_mutex); + + // get amount of data to write + UInt32 n = m_output->getSize(); + + // write data + const void* buffer = m_output->peek(n); + size_t n2 = ARCH->writeSocket(m_socket, buffer, n); + + // discard written data + if (n2 > 0) { + m_output->pop(n2); } } } } + catch (XArchNetwork&) { + // socket has failed + return; + } } } @@ -330,14 +356,24 @@ void CTCPSocket::closeInput(void*) { // note -- m_mutex should already be locked - CNetwork::shutdown(m_fd, 0); - m_connected &= ~kRead; + try { + ARCH->closeSocketForRead(m_socket); + m_connected &= ~kRead; + } + catch (XArchNetwork&) { + // ignore + } } void CTCPSocket::closeOutput(void*) { // note -- m_mutex should already be locked - CNetwork::shutdown(m_fd, 1); - m_connected &= ~kWrite; + try { + ARCH->closeSocketForWrite(m_socket); + m_connected &= ~kWrite; + } + catch (XArchNetwork&) { + // ignore + } } diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index 8aafa19c..bfbade89 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -16,11 +16,10 @@ #define CTCPSOCKET_H #include "IDataSocket.h" -#include "CNetwork.h" +#include "BasicTypes.h" +#include "IArchNetwork.h" class CMutex; -template -class CCondVar; class CThread; class CBufferedInputStream; class CBufferedOutputStream; @@ -32,7 +31,7 @@ A data socket using TCP. class CTCPSocket : public IDataSocket { public: CTCPSocket(); - CTCPSocket(CNetwork::Socket); + CTCPSocket(CArchSocket); ~CTCPSocket(); // ISocket overrides @@ -55,7 +54,7 @@ private: private: enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; - CNetwork::Socket m_fd; + CArchSocket m_socket; CBufferedInputStream* m_input; CBufferedOutputStream* m_output; diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index 899fa938..efeb5198 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -25,14 +25,11 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libnet.a libnet_a_SOURCES = \ - CNetwork.cpp \ CNetworkAddress.cpp \ CTCPListenSocket.cpp \ CTCPSocket.cpp \ CTCPSocketFactory.cpp \ - XNetwork.cpp \ XSocket.cpp \ - CNetwork.h \ CNetworkAddress.h \ CTCPListenSocket.h \ CTCPSocket.h \ @@ -41,10 +38,11 @@ libnet_a_SOURCES = \ IListenSocket.h \ ISocket.h \ ISocketFactory.h \ - XNetwork.h \ XSocket.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/net/XNetwork.cpp b/lib/net/XNetwork.cpp deleted file mode 100644 index 61ba2555..00000000 --- a/lib/net/XNetwork.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "XNetwork.h" - -// -// XNetworkUnavailable -// - -CString -XNetworkUnavailable::getWhat() const throw() -{ - return format("XNetworkUnavailable", "network library is not available"); -} - - -// -// XNetworkFailed -// - -CString -XNetworkFailed::getWhat() const throw() -{ - return format("XNetworkFailed", "cannot initialize network library"); -} - - -// -// XNetworkVersion -// - -XNetworkVersion::XNetworkVersion(int major, int minor) throw() : - m_major(major), - m_minor(minor) -{ - // do nothing -} - -int -XNetworkVersion::getMajor() const throw() -{ - return m_major; -} - -int -XNetworkVersion::getMinor() const throw() -{ - return m_minor; -} - -CString -XNetworkVersion::getWhat() const throw() -{ - return format("XNetworkVersion", - "unsupported network version %{1}.%{2}", - CStringUtil::print("%d", m_major).c_str(), - CStringUtil::print("%d", m_minor).c_str()); -} - - -// -// XNetworkFunctionUnavailable -// - -XNetworkFunctionUnavailable::XNetworkFunctionUnavailable( - const char* name) throw() -{ - try { - m_name = name; - } - catch (...) { - // ignore - } -} - -CString -XNetworkFunctionUnavailable::getWhat() const throw() -{ - return format("XNetworkFunctionUnavailable", - "missing network function %{1}", - m_name.c_str()); -} diff --git a/lib/net/XNetwork.h b/lib/net/XNetwork.h deleted file mode 100644 index 1e9eee5b..00000000 --- a/lib/net/XNetwork.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef XNETWORK_H -#define XNETWORK_H - -#include "XBase.h" -#include "CString.h" - -//! Generic network exception -/*! -Network exceptions are thrown when initializing the network subsystem -and not during normal network use. -*/ -class XNetwork : public XBase { }; - -//! Network subsystem not available exception -/*! -Thrown when the network subsystem is unavailable, typically because -the necessary shared library is unavailable. -*/ -class XNetworkUnavailable : public XNetwork { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; - -//! Network subsystem failed exception -/*! -Thrown when the network subsystem cannot be initialized. -*/ -class XNetworkFailed : public XNetwork { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; - -//! Network subsystem vesion unsupported exception -/*! -Thrown when the network subsystem has a version incompatible with -what's expected. -*/ -class XNetworkVersion : public XNetwork { -public: - XNetworkVersion(int major, int minor) throw(); - - //! @name accessors - //@{ - - //! Get the network subsystem's major version - int getMajor() const throw(); - //! Get the network subsystem's minor version - int getMinor() const throw(); - - //@} - -protected: - // XBase overrides - virtual CString getWhat() const throw(); - -private: - int m_major; - int m_minor; -}; - -//! Network subsystem incomplete exception -/*! -Thrown when the network subsystem is missing an expected and required -function. -*/ -class XNetworkFunctionUnavailable : public XNetwork { -public: - XNetworkFunctionUnavailable(const char* name) throw(); - -protected: - // XBase overrides - virtual CString getWhat() const throw(); - -private: - CString m_name; -}; - -#endif diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp index 8d372675..cf8b3ca6 100644 --- a/lib/net/XSocket.cpp +++ b/lib/net/XSocket.cpp @@ -13,14 +13,14 @@ */ #include "XSocket.h" -#include "CNetwork.h" +#include "CStringUtil.h" // // XSocketAddress // XSocketAddress::XSocketAddress(EError error, - const CString& hostname, UInt16 port) throw() : + const CString& hostname, int port) throw() : m_error(error), m_hostname(hostname), m_port(port) @@ -40,7 +40,7 @@ XSocketAddress::getHostname() const throw() return m_hostname; } -UInt16 +int XSocketAddress::getPort() const throw() { return m_port; @@ -67,97 +67,6 @@ XSocketAddress::getWhat() const throw() } -// -// XSocketErrno -// - -XSocketErrno::XSocketErrno() : - MXErrno(CNetwork::getsockerror()) -{ - // do nothing -} - -XSocketErrno::XSocketErrno(int err) : - MXErrno(err) -{ - // do nothing -} - -const char* -XSocketErrno::getErrstr() const -{ -#if WINDOWS_LIKE - // built-in windows function for looking up error message strings - // may not look up network error messages correctly. we'll have - // to do it ourself. - static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { - /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, - /* 10009 */{WSAEBADF, "Bad file handle"}, - /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, - /* 10014 */{WSAEFAULT, "WSAEFAULT"}, - /* 10022 */{WSAEINVAL, "WSAEINVAL"}, - /* 10024 */{WSAEMFILE, "No more file descriptors available"}, - /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, - /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, - /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, - /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, - /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, - /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, - /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, - /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, - /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, - /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, - /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, - /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, - /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, - /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, - /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, - /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, - /* 10051 */{WSAENETUNREACH, "The network can't be reached from this hos at this time"}, - /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, - /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, - /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, - /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, - /* 10056 */{WSAEISCONN, "The socket is already connected"}, - /* 10057 */{WSAENOTCONN, "The socket is not connected"}, - /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, - /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, - /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, - /* 10061 */{WSAECONNREFUSED, "The attempt to connect was forcefully rejected"}, - /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, - /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, - /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, - /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, - /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, - /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, - /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, - /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, - /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, - /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, - /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, - /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, - /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, - /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, - /* 11001 */{WSAHOST_NOT_FOUND, "Host not found"}, - /* 11002 */{WSATRY_AGAIN, "Host not found"}, - /* 11003 */{WSANO_RECOVERY, "Host name lookup error"}, - /* 11004 */{WSANO_DATA, "No data for host"}, - /* end */{0, NULL} - }; - - const int err = getErrno(); - for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { - if (s_netErrorCodes[i].m_code == err) { - return s_netErrorCodes[i].m_msg; - } - } -#endif - - // not a network error code. fallback to system error message. - return MXErrno::getErrstr(); -} - - // // XSocketIOClose // @@ -165,8 +74,7 @@ XSocketErrno::getErrstr() const CString XSocketIOClose::getWhat() const throw() { - return format("XSocketIOClose", "close: %{1}", - getErrstr()); + return format("XSocketIOClose", "close: %{1}", what()); } @@ -177,8 +85,7 @@ XSocketIOClose::getWhat() const throw() CString XSocketBind::getWhat() const throw() { - return format("XSocketBind", "cannot bind address: %{1}", - getErrstr()); + return format("XSocketBind", "cannot bind address: %{1}", what()); } @@ -189,8 +96,7 @@ XSocketBind::getWhat() const throw() CString XSocketConnect::getWhat() const throw() { - return format("XSocketConnect", "cannot connect socket: %{1}", - getErrstr()); + return format("XSocketConnect", "cannot connect socket: %{1}", what()); } @@ -201,6 +107,5 @@ XSocketConnect::getWhat() const throw() CString XSocketCreate::getWhat() const throw() { - return format("XSocketCreate", "cannot create socket: %{1}", - getErrstr()); + return format("XSocketCreate", "cannot create socket: %{1}", what()); } diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h index 131a3f1f..c3663033 100644 --- a/lib/net/XSocket.h +++ b/lib/net/XSocket.h @@ -21,7 +21,7 @@ #include "BasicTypes.h" //! Generic socket exception -class XSocket : public XBase { }; +XBASE_SUBCLASS(XSocket, XBase); //! Socket bad address exception /*! @@ -37,7 +37,7 @@ public: kBadPort //!< The port is invalid }; - XSocketAddress(EError, const CString& hostname, UInt16 port) throw(); + XSocketAddress(EError, const CString& hostname, int port) throw(); //! @name accessors //@{ @@ -47,7 +47,7 @@ public: //! Get the hostname CString getHostname() const throw(); //! Get the port - UInt16 getPort() const throw(); + int getPort() const throw(); //@} @@ -58,80 +58,38 @@ protected: private: EError m_error; CString m_hostname; - UInt16 m_port; -}; - -//! Generic socket exception using \c errno -class XSocketErrno : public MXErrno { -public: - XSocketErrno(); - XSocketErrno(int); - - // MXErrno overrides - virtual const char* getErrstr() const; + int m_port; }; //! I/O closing exception /*! Thrown if a stream cannot be closed. */ -class XSocketIOClose : public XIOClose, public XSocketErrno { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketIOClose, XIOClose); //! Socket cannot bind address exception /*! Thrown when a socket cannot be bound to an address. */ -class XSocketBind : public XSocket, public XSocketErrno { -public: - XSocketBind() { } - XSocketBind(int e) : XSocketErrno(e) { } - -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketBind, XSocket); //! Socket address in use exception /*! Thrown when a socket cannot be bound to an address because the address is already in use. */ -class XSocketAddressInUse : public XSocketBind { -public: - XSocketAddressInUse() { } - XSocketAddressInUse(int e) : XSocketBind(e) { } -}; +XBASE_SUBCLASS(XSocketAddressInUse, XSocketBind); //! Cannot connect socket exception /*! Thrown when a socket cannot connect to a remote endpoint. */ -class XSocketConnect : public XSocket, public XSocketErrno { -public: - XSocketConnect() { } - XSocketConnect(int e) : XSocketErrno(e) { } - -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketConnect, XSocket); //! Cannot create socket exception /*! Thrown when a socket cannot be created (by the operating system). */ -class XSocketCreate : public XSocket, public XSocketErrno { -public: - XSocketCreate() { } - XSocketCreate(int e) : XSocketErrno(e) { } - -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketCreate, XSocket); #endif diff --git a/lib/net/net.dsp b/lib/net/net.dsp index fb7df902..2d038c50 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -87,10 +87,6 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\CNetwork.cpp -# End Source File -# Begin Source File - SOURCE=.\CNetworkAddress.cpp # End Source File # Begin Source File @@ -107,10 +103,6 @@ SOURCE=.\CTCPSocketFactory.cpp # End Source File # Begin Source File -SOURCE=.\XNetwork.cpp -# End Source File -# Begin Source File - SOURCE=.\XSocket.cpp # End Source File # End Group @@ -119,10 +111,6 @@ SOURCE=.\XSocket.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\CNetwork.h -# End Source File -# Begin Source File - SOURCE=.\CNetworkAddress.h # End Source File # Begin Source File @@ -151,10 +139,6 @@ SOURCE=.\ISocketFactory.h # End Source File # Begin Source File -SOURCE=.\XNetwork.h -# End Source File -# Begin Source File - SOURCE=.\XSocket.h # End Source File # End Group diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 1b8e74c7..962b9047 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -15,7 +15,6 @@ #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" -#include "CPlatform.h" #include "CClipboard.h" #include "IMSWindowsScreenEventHandler.h" #include "IScreenReceiver.h" @@ -24,6 +23,8 @@ #include "TMethodJob.h" #include "CLog.h" #include "CString.h" +#include "CStringUtil.h" +#include "CArchMiscWindows.h" #include // @@ -49,7 +50,7 @@ CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, m_class(NULL), m_icon(NULL), m_cursor(NULL), - m_is95Family(CPlatform::isWindows95Family()), + m_is95Family(CArchMiscWindows::isWindows95Family()), m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), diff --git a/lib/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp deleted file mode 100644 index 02848633..00000000 --- a/lib/platform/CUnixPlatform.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CUnixPlatform.h" -#include "CLog.h" -#include -#include -#include -#include -#include -#include -#include - - -// -// CUnixPlatform -// - -CUnixPlatform::CUnixPlatform() -{ - // do nothing -} - -CUnixPlatform::~CUnixPlatform() -{ - // do nothing -} - -bool -CUnixPlatform::installDaemon(const char*, const char*, - const char*, const char*, bool) -{ - // daemons don't require special installation - return true; -} - -CUnixPlatform::EResult -CUnixPlatform::uninstallDaemon(const char*, bool) -{ - // daemons don't require special installation - return kSuccess; -} - -int -CUnixPlatform::daemonize(const char* name, DaemonFunc func) -{ - // fork so shell thinks we're done and so we're not a process - // group leader - switch (fork()) { - case -1: - // failed - return -1; - - case 0: - // child - break; - - default: - // parent exits - exit(0); - } - - // become leader of a new session - setsid(); - - // chdir to root so we don't keep mounted filesystems points busy - chdir("/"); - - // mask off permissions for any but owner - umask(077); - - // close open files. we only expect stdin, stdout, stderr to be open. - close(0); - close(1); - close(2); - - // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use - // of standard I/O safely goes in the bit bucket. - open("/dev/null", O_RDONLY); - open("/dev/null", O_RDWR); - dup(1); - - // hook up logger - installDaemonLogger(name); - - // invoke function - return func(this, 1, &name); -} - -void -CUnixPlatform::installDaemonLogger(const char* name) -{ - openlog(name, 0, LOG_DAEMON); - CLog::setOutputter(&CUnixPlatform::deamonLogger); -} - -bool -CUnixPlatform::canInstallDaemon(const char*, bool) const -{ - return false; -} - -bool -CUnixPlatform::isDaemonInstalled(const char*, bool) const -{ - return false; -} - -const char* -CUnixPlatform::getBasename(const char* pathname) const -{ - if (pathname == NULL) { - return NULL; - } - - const char* basename = strrchr(pathname, '/'); - if (basename != NULL) { - return basename + 1; - } - else { - return pathname; - } -} - -CString -CUnixPlatform::getUserDirectory() const -{ -#if HAVE_GETPWUID_R - struct passwd pwent; - struct passwd* pwentp; -#if defined(_SC_GETPW_R_SIZE_MAX) - long size = sysconf(_SC_GETPW_R_SIZE_MAX); -#else - long size = BUFSIZ; -#endif - char* buffer = new char[size]; - getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); - delete[] buffer; -#else - struct passwd* pwentp = getpwuid(getuid()); -#endif - if (pwentp != NULL && pwentp->pw_dir != NULL) { - return pwentp->pw_dir; - } - else { - return CString(); - } -} - -CString -CUnixPlatform::getSystemDirectory() const -{ - return "/etc"; -} - -CString -CUnixPlatform::addPathComponent(const CString& prefix, - const CString& suffix) const -{ - CString path; - path.reserve(prefix.size() + 1 + suffix.size()); - path += prefix; - if (path.size() == 0 || path[path.size() - 1] != '/') { - path += '/'; - } - path += suffix; - return path; -} - -bool -CUnixPlatform::deamonLogger(int priority, const char* msg) -{ - // convert priority - switch (priority) { - case CLog::kFATAL: - case CLog::kERROR: - priority = LOG_ERR; - break; - - case CLog::kWARNING: - priority = LOG_WARNING; - break; - - case CLog::kNOTE: - priority = LOG_NOTICE; - break; - - case CLog::kINFO: - priority = LOG_INFO; - break; - - default: - priority = LOG_DEBUG; - break; - } - - // log it - syslog(priority, "%s", msg); - return true; -} diff --git a/lib/platform/CUnixPlatform.h b/lib/platform/CUnixPlatform.h deleted file mode 100644 index 00e5ea23..00000000 --- a/lib/platform/CUnixPlatform.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CUNIXPLATFORM_H -#define CUNIXPLATFORM_H - -#include "IPlatform.h" - -//! Unix platform dependent functions -class CUnixPlatform : public IPlatform { -public: - CUnixPlatform(); - virtual ~CUnixPlatform(); - - // IPlatform overrides - virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine, - bool allUsers); - virtual EResult uninstallDaemon(const char* name, bool allUsers); - virtual int daemonize(const char* name, DaemonFunc); - virtual void installDaemonLogger(const char* name); - virtual bool canInstallDaemon(const char* name, - bool allUsers) const; - virtual bool isDaemonInstalled(const char* name, - bool allUsers) const; - virtual const char* getBasename(const char* pathname) const; - virtual CString getUserDirectory() const; - virtual CString getSystemDirectory() const; - virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const; - -private: - static bool deamonLogger(int, const char*); -}; - -#endif diff --git a/lib/platform/CWin32Platform.h b/lib/platform/CWin32Platform.h deleted file mode 100644 index dadcb4f2..00000000 --- a/lib/platform/CWin32Platform.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CWIN32PLATFORM_H -#define CWIN32PLATFORM_H - -#include "IPlatform.h" -#include "CCondVar.h" -#include "CMutex.h" -#define WIN32_LEAN_AND_MEAN -#include - -//! Microsoft windows platform dependent functions -class CWin32Platform : public IPlatform { -public: - typedef int (*RunFunc)(CMutex*); - typedef void (*StopFunc)(void); - - CWin32Platform(); - virtual ~CWin32Platform(); - - //! Test if windows 95, et al. - /*! - Returns true iff the platform is win95/98/me. - */ - static bool isWindows95Family(); - - //! Utility for calling SetServiceStatus() - static void setStatus(SERVICE_STATUS_HANDLE, DWORD state); - //! Utility for calling SetServiceStatus() - static void setStatus(SERVICE_STATUS_HANDLE, - DWORD state, DWORD step, DWORD waitHint); - //! Utility for calling SetServiceStatus() - static void setStatusError(SERVICE_STATUS_HANDLE, DWORD error); - - //! Run service - /*! - Run a service. The RunFunc should unlock the passed in mutex - (which will be locked on entry) when not initializing or - shutting down (i.e. when running its loop). StopFunc should - cause the RunFunc() to return. Returns what RunFunc returns. - RunFunc should throw CDaemonFailed if the service fails. - */ - int runDaemon(RunFunc, StopFunc); - - //! Daemon failed exception - /*! - Thrown by RunFunc on service failure. Result is the error - code reported by the service. - */ - class CDaemonFailed { - public: - CDaemonFailed(int result) : m_result(result) { } - - public: - int m_result; - }; - - // IPlatform overrides - virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine, - bool allUsers); - virtual EResult uninstallDaemon(const char* name, bool allUsers); - virtual int daemonize(const char* name, DaemonFunc); - virtual void installDaemonLogger(const char* name); - virtual bool canInstallDaemon(const char* name, - bool allUsers) const; - virtual bool isDaemonInstalled(const char* name, - bool allUsers) const; - virtual const char* getBasename(const char* pathname) const; - virtual CString getUserDirectory() const; - virtual CString getSystemDirectory() const; - virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const; - -private: - static HKEY openKey(HKEY parent, const char*); - static HKEY openKey(HKEY parent, const char**); - static void closeKey(HKEY); - static void deleteKey(HKEY, const char* name); - static void deleteValue(HKEY, const char* name); - static void setValue(HKEY, const char* name, - const CString& value); - static CString readValueString(HKEY, const char* name); - static HKEY openNTServicesKey(); - static HKEY open95ServicesKey(); - static HKEY openUserStartupKey(); - - void serviceMain(DWORD, LPTSTR*); - static void WINAPI serviceMainEntry(DWORD, LPTSTR*); - - void runDaemonThread(void*); - - void serviceHandler(DWORD ctrl); - static void WINAPI serviceHandlerEntry(DWORD ctrl); - - static bool serviceLogger(int, const char*); - -private: - DaemonFunc m_daemonFunc; - int m_daemonResult; - - SERVICE_STATUS_HANDLE m_statusHandle; - - CMutex* m_serviceMutex; - CCondVar* m_serviceState; - bool m_serviceHandlerWaiting; - bool m_serviceRunning; - StopFunc m_stop; - - static HANDLE s_eventLog; - - static CWin32Platform* s_daemonPlatform; -}; - -#endif diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 32ae8cde..6a9ced0f 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -20,6 +20,7 @@ #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" +#include "CArch.h" #include "stdvector.h" #include #include @@ -751,16 +752,16 @@ CXWindowsClipboard::motifFillCache() } // see if atom is in target list - CMotifFormatMap::const_iterator index = + CMotifFormatMap::const_iterator index2 = motifFormats.find(converter->getAtom()); - if (index == motifFormats.end()) { + if (index2 == motifFormats.end()) { continue; } // get format const CMotifClipFormat* motifFormat = reinterpret_cast( - index->second.data()); + index2->second.data()); const Atom target = motifFormat->m_type; // get the data (finally) @@ -1283,7 +1284,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, } } else { - CThread::sleep(0.01); + ARCH->sleep(0.01); } } diff --git a/lib/platform/IPlatform.h b/lib/platform/IPlatform.h deleted file mode 100644 index 2962f4f2..00000000 --- a/lib/platform/IPlatform.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef IPLATFORM_H -#define IPLATFORM_H - -#include "IInterface.h" -#include "CString.h" - -//! Platform dependent functions interface -/*! -This interface provides methods to query platform specific data and -perform operations that are inherently non-portable. -*/ -class IPlatform : public IInterface { -public: - typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); - - //! Uninstall result codes - enum EResult { - kSuccess, //!< Uninstall succeeded - kFailed, //!< Uninstall failed - kAlready //!< Not installed - }; - - //! @name manipulators - //@{ - - //! Install daemon - /*! - Install a daemon. \c name is the name of the daemon passed to the - system and \c description is a short human readable description of - the daemon. \c pathname is the path to the daemon executable. - \c commandLine should \b not include the name of program as the - first argument. If \c allUsers is true then the daemon will be - installed to start at boot time, otherwise it will be installed to - start when the current user logs in. - */ - // FIXME -- throw on error? will get better error messages that way. - virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine, - bool allUsers) = 0; - - //! Uninstall daemon - /*! - Uninstall a daemon. - */ - virtual EResult uninstallDaemon(const char* name, bool allUsers) = 0; - - //! Daemonize the process - /*! - Daemonize. This should call installDaemonLogger(). Returns - true iff successful. \c name is the name of the daemon. - Once daemonized, \c func is invoked and daemonize returns when - and what it does. daemonize() returns -1 on error. - - Exactly what happens when daemonizing depends on the platform. -
    -
  • unix: - Detaches from terminal. \c func gets passed one argument, the - name passed to daemonize(). -
  • win32: - Becomes a service. Argument 0 is the name of the service - and the rest are the arguments passed to StartService(). - \c func is only called when the service is actually started. - \c func must behave like a proper ServiceMain() function; in - particular, it must call RegisterServiceCtrlHandler() and - SetServiceStatus(). -
- */ - virtual int daemonize(const char* name, DaemonFunc func) = 0; - - //! Install daemon logger - /*! - Directs CLog to send messages to the daemon log. Used when - messages should no longer go to the console. \c name is used - in the log to identify this process. - */ - virtual void installDaemonLogger(const char* name) = 0; - - //@} - //! @name accessors - //@{ - - //! Check if user has permission to install the daemon - /*! - Returns true iff the caller has permission to install or - uninstall the daemon. Note that even if this method returns - true it's possible that installing/uninstalling the service - may still fail. This method ignores whether or not the - service is already installed. - */ - virtual bool canInstallDaemon(const char* name, - bool allUsers) const = 0; - - //! Check if the daemon is installed - /*! - Returns true iff the daemon is installed. - */ - virtual bool isDaemonInstalled(const char* name, - bool allUsers) const = 0; - - //! Extract base name - /*! - Find the base name in the given \c pathname. - */ - virtual const char* getBasename(const char* pathname) const = 0; - - //! Get user's home directory - /*! - Returns the user's home directory. Returns the empty string if - this cannot be determined. - */ - virtual CString getUserDirectory() const = 0; - - //! Get system directory - /*! - Returns the ussystem configuration file directory. - */ - virtual CString getSystemDirectory() const = 0; - - //! Concatenate path components - /*! - Concatenate pathname components with a directory separator - between them. This should not check if the resulting path - is longer than allowed by the system; we'll rely on the - system calls to tell us that. - */ - virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const = 0; - - //@} -}; - -#endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 53511b5d..e6c6d6b6 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -26,7 +26,6 @@ EXTRA_DIST = \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ CSynergyHook.cpp \ - CWin32Platform.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ CMSWindowsClipboardTextConverter.h \ @@ -34,7 +33,6 @@ EXTRA_DIST = \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ CSynergyHook.h \ - CWin32Platform.h \ IMSWindowsScreenEventHandler.h \ $(NULL) @@ -44,7 +42,6 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libplatform.a libplatform_a_SOURCES = \ - CPlatform.cpp \ CXWindowsClipboard.cpp \ CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardUCS2Converter.cpp \ @@ -52,8 +49,6 @@ libplatform_a_SOURCES = \ CXWindowsScreen.cpp \ CXWindowsScreenSaver.cpp \ CXWindowsUtil.cpp \ - CPlatform.h \ - CUnixPlatform.h \ CXWindowsClipboard.h \ CXWindowsClipboardTextConverter.h \ CXWindowsClipboardUCS2Converter.h \ @@ -61,12 +56,10 @@ libplatform_a_SOURCES = \ CXWindowsScreen.h \ CXWindowsScreenSaver.h \ CXWindowsUtil.h \ - IPlatform.h \ - $(NULL) -EXTRA_libplatform_a_SOURCES = \ - CUnixPlatform.cpp \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index b05d246e..300f12cb 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -109,10 +109,6 @@ SOURCE=.\CMSWindowsScreen.cpp SOURCE=.\CMSWindowsScreenSaver.cpp # End Source File -# Begin Source File - -SOURCE=.\CPlatform.cpp -# End Source File # End Group # Begin Group "Header Files" @@ -143,33 +139,8 @@ SOURCE=.\CMSWindowsScreenSaver.h # End Source File # Begin Source File -SOURCE=.\CPlatform.h -# End Source File -# Begin Source File - -SOURCE=.\CWin32Platform.h -# End Source File -# Begin Source File - SOURCE=.\IMSWindowsScreenEventHandler.h # End Source File -# Begin Source File - -SOURCE=.\IPlatform.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# Begin Group "Included Files" - -# PROP Default_Filter "inc" -# Begin Source File - -SOURCE=.\CWin32Platform.cpp -# PROP Exclude_From_Build 1 -# End Source File # End Group # End Target # End Project diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index a7735c3e..cec6eaf9 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -82,7 +82,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"" /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /nodefaultlib !ENDIF diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index 6fce3dda..1e29b8a7 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -18,6 +18,7 @@ #include "OptionTypes.h" #include "ProtocolTypes.h" #include "CNetworkAddress.h" +#include "CStringUtil.h" #include "XBase.h" #include "stdmap.h" #include "stdset.h" diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 3e43ca51..56143f30 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -15,9 +15,9 @@ #include "CMSWindowsPrimaryScreen.h" #include "CMSWindowsScreen.h" #include "IPrimaryScreenReceiver.h" -#include "CPlatform.h" #include "XScreen.h" #include "CLog.h" +#include "CArchMiscWindows.h" #include // @@ -29,7 +29,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( IPrimaryScreenReceiver* primaryReceiver) : CPrimaryScreen(receiver), m_receiver(primaryReceiver), - m_is95Family(CPlatform::isWindows95Family()), + m_is95Family(CArchMiscWindows::isWindows95Family()), m_threadID(0), m_window(NULL), m_mark(0), diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index e08db389..0e101e9d 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -38,6 +38,7 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" +#include "CArch.h" // // CServer @@ -1074,7 +1075,7 @@ CServer::closeClients(const CConfig& config) // wait a moment to allow each client to close its connection // before we close it (to avoid having our socket enter TIME_WAIT). if (threads.size() > 0) { - CThread::sleep(1.0); + ARCH->sleep(1.0); } // cancel the old client threads @@ -1103,10 +1104,10 @@ CServer::startThread(IJob* job) // reap completed threads doReapThreads(m_threads); - // add new thread to list. use the job as user data for logging. - CThread thread(job, job); + // add new thread to list + CThread thread(job); m_threads.push_back(thread); - LOG((CLOG_DEBUG1 "started thread %p", thread.getUserData())); + LOG((CLOG_DEBUG1 "started thread 0x%08x", thread.getID())); return thread; } @@ -1152,13 +1153,13 @@ CServer::stopThreads(double timeout) CStopwatch timer(true); while (threads.size() > 0 && (timeout < 0.0 || timer.getTime() < timeout)) { doReapThreads(threads); - CThread::sleep(0.01); + ARCH->sleep(0.01); } // delete remaining threads for (CThreadList::iterator index = threads.begin(); index != threads.end(); ++index) { - LOG((CLOG_DEBUG1 "reaped running thread %p", index->getUserData())); + LOG((CLOG_DEBUG1 "reaped running thread 0x%08x", index->getID())); } LOG((CLOG_DEBUG1 "stopped threads")); @@ -1178,7 +1179,7 @@ CServer::doReapThreads(CThreadList& threads) index != threads.end(); ) { if (index->wait(0.0)) { // thread terminated - LOG((CLOG_DEBUG1 "reaped thread %p", index->getUserData())); + LOG((CLOG_DEBUG1 "reaped thread 0x%08x", index->getID())); index = threads.erase(index); } else { @@ -1211,7 +1212,7 @@ CServer::acceptClients(void*) break; } catch (XSocketAddressInUse& e) { - LOG((CLOG_WARN "bind failed: %s", e.getErrstr())); + LOG((CLOG_WARN "bind failed: %s", e.what())); // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { @@ -1220,7 +1221,7 @@ CServer::acceptClients(void*) } // wait a bit before retrying - CThread::sleep(5.0); + ARCH->sleep(5.0); } } @@ -1489,7 +1490,7 @@ CServer::acceptHTTPClients(void*) break; } catch (XSocketBind& e) { - LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.getErrstr())); + LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.what())); // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { @@ -1498,7 +1499,7 @@ CServer::acceptHTTPClients(void*) } // wait a bit before retrying - CThread::sleep(5.0); + ARCH->sleep(5.0); } } @@ -1550,7 +1551,7 @@ CServer::processHTTPRequest(void* vsocket) socket->getOutputStream()->flush(); // wait a moment to give the client a chance to hangup first - CThread::sleep(3.0); + ARCH->sleep(3.0); // clean up socket->close(); diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index b544235b..763cf4ae 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -29,6 +29,7 @@ # define XK_XKB_KEYS # include #endif +#include "CArch.h" // // CXWindowsPrimaryScreen @@ -517,7 +518,7 @@ CXWindowsPrimaryScreen::showWindow() assert(result != GrabNotViewable); if (result != GrabSuccess) { LOG((CLOG_DEBUG2 "waiting to grab keyboard")); - CThread::sleep(0.05); + ARCH->sleep(0.05); if (timer.getTime() >= s_timeout) { LOG((CLOG_DEBUG2 "grab keyboard timed out")); XUnmapWindow(display, m_window); @@ -536,7 +537,7 @@ CXWindowsPrimaryScreen::showWindow() // back off to avoid grab deadlock XUngrabKeyboard(display, CurrentTime); LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); - CThread::sleep(0.05); + ARCH->sleep(0.05); if (timer.getTime() >= s_timeout) { LOG((CLOG_DEBUG2 "grab pointer timed out")); XUnmapWindow(display, m_window); diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index e874aa37..5f4daf73 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -46,6 +46,8 @@ libserver_a_SOURCES = \ IPrimaryScreenFactory.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/server/server.dsp b/lib/server/server.dsp index fa732ab7..26134e1c 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index f7e5d64c..c86a8b34 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -52,6 +52,8 @@ libsynergy_a_SOURCES = \ XSynergy.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/synergy/XScreen.h b/lib/synergy/XScreen.h index 3bfa7eb2..9966e6cf 100644 --- a/lib/synergy/XScreen.h +++ b/lib/synergy/XScreen.h @@ -18,16 +18,13 @@ #include "XBase.h" //! Generic screen exception -class XScreen : public XBase { }; +XBASE_SUBCLASS(XScreen, XBase); //! Cannot open screen exception /*! Thrown when a screen cannot be opened or initialized. */ -class XScreenOpenFailure : public XScreen { -protected: - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XScreenOpenFailure, XScreen); //! Screen unavailable exception /*! diff --git a/lib/synergy/XSynergy.cpp b/lib/synergy/XSynergy.cpp index c50d542f..1e19945f 100644 --- a/lib/synergy/XSynergy.cpp +++ b/lib/synergy/XSynergy.cpp @@ -13,6 +13,7 @@ */ #include "XSynergy.h" +#include "CStringUtil.h" // // XBadClient diff --git a/lib/synergy/XSynergy.h b/lib/synergy/XSynergy.h index eb5b5167..23131194 100644 --- a/lib/synergy/XSynergy.h +++ b/lib/synergy/XSynergy.h @@ -18,16 +18,13 @@ #include "XBase.h" //! Generic synergy exception -class XSynergy : public XBase { }; +XBASE_SUBCLASS(XSynergy, XBase); //! Client error exception /*! Thrown when the client fails to follow the protocol. */ -class XBadClient : public XSynergy { -protected: - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XBadClient, XSynergy); //! Incompatible client exception /*! diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index b53b957d..3b121f79 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -4,7 +4,7 @@ # TARGTYPE "Win32 (x86) Static Library" 0x0104 -CFG=synergy - Win32 Debug +CFG=libsynergy - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE @@ -13,7 +13,7 @@ CFG=synergy - Win32 Debug !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE -!MESSAGE NMAKE /f "libsynergy.mak" CFG="libsynergy - Win32 Debug" +!MESSAGE NMAKE /f "libsynergy.mak" CFG="libsynergy - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/synergy.dsw b/synergy.dsw index baae1dce..a70a4d76 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -24,6 +24,18 @@ Package=<4> ############################################################################### +Project: "arch"=.\lib\arch\arch.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "base"=.\lib\base\base.dsp - Package Owner=<4> Package=<5> @@ -48,6 +60,18 @@ Package=<4> ############################################################################### +Project: "common"=.\lib\common\common.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "http"=.\lib\HTTP\http.dsp - Package Owner=<4> Package=<5> @@ -80,6 +104,12 @@ Package=<5> Package=<4> {{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency Begin Project Dependency Project_Dep_Name base End Project Dependency @@ -188,6 +218,12 @@ Package=<5> Package=<4> {{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency Begin Project Dependency Project_Dep_Name base End Project Dependency @@ -221,6 +257,12 @@ Package=<5> Package=<4> {{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency Begin Project Dependency Project_Dep_Name base End Project Dependency