From 82dffeb49850e197e4e6ce21850b532768e0ecae Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 17:49:29 +0000 Subject: [PATCH] Generalized signal handling. Now handling SIGHUP in addition to SIGINT and SIGTERM. Setup SIGHUP to reload the server's configuration. --- cmd/synergyc/synergyc.cpp | 9 +++- cmd/synergys/synergys.cpp | 77 +++++++++++++++++++------- lib/arch/CArch.cpp | 8 +-- lib/arch/CArch.h | 4 +- lib/arch/CArchConsoleWindows.cpp | 17 ++++-- lib/arch/CArchMultithreadPosix.cpp | 80 ++++++++++++++++------------ lib/arch/CArchMultithreadPosix.h | 8 +-- lib/arch/CArchMultithreadWindows.cpp | 25 +++++---- lib/arch/CArchMultithreadWindows.h | 8 +-- lib/arch/IArchMultithread.h | 25 +++++++-- lib/base/CEventQueue.cpp | 10 ++-- 11 files changed, 183 insertions(+), 88 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 7ca94409..165c246c 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -358,6 +358,13 @@ static int mainLoop() { + // create socket multiplexer. this must happen after daemonization + // on unix because threads evaporate across a fork(). + CSocketMultiplexer multiplexer; + + // create the event queue + CEventQueue eventQueue; + // start the client. if this return false then we've failed and // we shouldn't retry. LOG((CLOG_DEBUG1 "starting client")); @@ -416,8 +423,6 @@ int run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) { // general initialization - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; ARG->m_serverAddress = new CNetworkAddress; ARG->m_pname = ARCH->getBasename(argv[0]); diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index d1900fea..2f57aca7 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -68,6 +68,7 @@ typedef int (*StartupFunc)(int, char**); static void parse(int argc, const char* const* argv); +static bool loadConfig(const CString& pathname); static void loadConfig(); // @@ -83,7 +84,7 @@ public: m_backend(false), m_restartable(true), m_daemon(true), - m_configFile(NULL), + m_configFile(), m_logFilter(NULL) { s_instance = this; } ~CArgs() { s_instance = NULL; } @@ -94,7 +95,7 @@ public: bool m_backend; bool m_restartable; bool m_daemon; - const char* m_configFile; + CString m_configFile; const char* m_logFilter; CString m_name; CNetworkAddress* m_synergyAddress; @@ -136,11 +137,12 @@ createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) // platform independent main // -static CServer* s_server = NULL; -static CScreen* s_serverScreen = NULL; -static CPrimaryClient* s_primaryClient = NULL; -static CClientListener* s_listener = NULL; -static CServerTaskBarReceiver* s_taskBarReceiver = NULL; +static CServer* s_server = NULL; +static CScreen* s_serverScreen = NULL; +static CPrimaryClient* s_primaryClient = NULL; +static CClientListener* s_listener = NULL; +static CServerTaskBarReceiver* s_taskBarReceiver = NULL; +static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; static void @@ -389,10 +391,38 @@ stopServer() s_serverScreen = NULL; } +static +void +reloadSignalHandler(CArch::ESignal, void*) +{ + EVENTQUEUE->addEvent(CEvent(s_reloadConfigEvent, + IEventQueue::getSystemTarget())); +} + +static +void +reloadConfig(const CEvent&, void*) +{ + LOG((CLOG_DEBUG "reload configuration")); + if (loadConfig(ARG->m_configFile)) { + if (s_server != NULL) { + s_server->setConfig(*ARG->m_config); + } + LOG((CLOG_NOTE "reloaded configuration")); + } +} + static int mainLoop() { + // create socket multiplexer. this must happen after daemonization + // on unix because threads evaporate across a fork(). + CSocketMultiplexer multiplexer; + + // create the event queue + CEventQueue eventQueue; + // if configuration has no screens then add this system // as the default if (ARG->m_config->begin() == ARG->m_config->end()) { @@ -423,6 +453,13 @@ mainLoop() return kExitFailed; } + // handle hangup signal by reloading the server's configuration + CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig"); + ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL); + EVENTQUEUE->adoptHandler(s_reloadConfigEvent, + IEventQueue::getSystemTarget(), + new CFunctionEventJob(&reloadConfig)); + // run event loop. if startServer() failed we're supposed to retry // later. the timer installed by startServer() will take care of // that. @@ -438,6 +475,8 @@ mainLoop() // close down LOG((CLOG_DEBUG1 "stopping server")); + EVENTQUEUE->removeHandler(s_reloadConfigEvent, + IEventQueue::getSystemTarget()); stopServer(); updateStatus(); LOG((CLOG_NOTE "stopped server")); @@ -477,8 +516,6 @@ int run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) { // general initialization - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; ARG->m_synergyAddress = new CNetworkAddress; ARG->m_config = new CConfig; ARG->m_pname = ARCH->getBasename(argv[0]); @@ -759,14 +796,12 @@ parse(int argc, const char* const* argv) static bool -loadConfig(const char* pathname) +loadConfig(const CString& pathname) { - assert(pathname != NULL); - try { // load configuration - LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname)); - std::ifstream configStream(pathname); + LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str())); + std::ifstream configStream(pathname.c_str()); if (!configStream) { throw XConfigRead("cannot open file"); } @@ -776,7 +811,7 @@ loadConfig(const char* pathname) } catch (XConfigRead& e) { LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", - pathname, e.what())); + pathname.c_str(), e.what())); } return false; } @@ -788,7 +823,7 @@ loadConfig() bool loaded = false; // load the config file, if specified - if (ARG->m_configFile != NULL) { + if (!ARG->m_configFile.empty()) { loaded = loadConfig(ARG->m_configFile); } @@ -801,14 +836,20 @@ loadConfig() path = ARCH->concatPath(path, USR_CONFIG_NAME); // now try loading the user's configuration - loaded = loadConfig(path.c_str()); + if (loadConfig(path)) { + loaded = true; + ARG->m_configFile = path; + } } if (!loaded) { // try the system-wide config file path = ARCH->getSystemDirectory(); if (!path.empty()) { path = ARCH->concatPath(path, SYS_CONFIG_NAME); - loaded = loadConfig(path.c_str()); + if (loadConfig(path)) { + loaded = true; + ARG->m_configFile = path; + } } } } diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index d8640960..98b18279 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -376,15 +376,15 @@ CArch::getIDOfThread(CArchThread thread) } void -CArch::setInterruptHandler(InterruptFunc func, void* userData) +CArch::setSignalHandler(ESignal signal, SignalFunc func, void* userData) { - m_mt->setInterruptHandler(func, userData); + m_mt->setSignalHandler(signal, func, userData); } void -CArch::interrupt() +CArch::raiseSignal(ESignal signal) { - m_mt->interrupt(); + m_mt->raiseSignal(signal); } CArchSocket diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index f82c27cf..8152d669 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -120,8 +120,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); - virtual void setInterruptHandler(InterruptFunc, void*); - virtual void interrupt(); + virtual void setSignalHandler(ESignal, SignalFunc, void*); + virtual void raiseSignal(ESignal); // IArchNetwork overrides virtual CArchSocket newSocket(EAddressFamily, ESocketType); diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index 2cdedc71..6796a454 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -108,9 +108,20 @@ CArchConsoleWindows::getNewlineForConsole() } BOOL WINAPI -CArchConsoleWindows::signalHandler(DWORD) +CArchConsoleWindows::signalHandler(DWORD ctrlType) { // terminate app and skip remaining handlers - ARCH->interrupt(); - return TRUE; + switch (ctrlType) { + case CTRL_C_EVENT: + ARCH->raiseSignal(CArch::kINTERRUPT); + return TRUE; + + case CTRL_BREAK_EVENT: + ARCH->raiseSignal(CArch::kTERMINATE); + return TRUE; + + default: + ARCH->raiseSignal(CArch::kINTERRUPT); + return TRUE; + } } diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 403cc82b..b87a9b58 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -41,6 +41,16 @@ # define HAVE_POSIX_SIGWAIT 1 #endif +static +void +setSignalSet(sigset_t* sigset) +{ + sigemptyset(sigset); + sigaddset(sigset, SIGHUP); + sigaddset(sigset, SIGINT); + sigaddset(sigset, SIGTERM); +} + // // CArchThreadImpl // @@ -83,14 +93,18 @@ CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; CArchMultithreadPosix::CArchMultithreadPosix() : m_newThreadCalled(false), - m_nextID(0), - m_signalFunc(NULL), - m_signalUserData(NULL) + m_nextID(0) { assert(s_instance == NULL); s_instance = this; + // no signal handlers + for (size_t i = 0; i < kNUM_SIGNALS; ++i) { + m_signalFunc[i] = NULL; + m_signalUserData[i] = NULL; + } + // create mutex for thread list m_threadMutex = newMutex(); @@ -353,13 +367,6 @@ CArchMultithreadPosix::newThread(ThreadFunc func, void* data) 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; @@ -370,9 +377,6 @@ CArchMultithreadPosix::newThread(ThreadFunc func, void* data) 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 @@ -560,23 +564,24 @@ CArchMultithreadPosix::getIDOfThread(CArchThread thread) } void -CArchMultithreadPosix::setInterruptHandler(InterruptFunc func, void* userData) +CArchMultithreadPosix::setSignalHandler( + ESignal signal, SignalFunc func, void* userData) { lockMutex(m_threadMutex); - m_signalFunc = func; - m_signalUserData = userData; + m_signalFunc[signal] = func; + m_signalUserData[signal] = userData; unlockMutex(m_threadMutex); } void -CArchMultithreadPosix::interrupt() +CArchMultithreadPosix::raiseSignal(ESignal signal) { lockMutex(m_threadMutex); - if (m_signalFunc != NULL) { - m_signalFunc(m_signalUserData); + if (m_signalFunc[signal] != NULL) { + m_signalFunc[signal](signal, m_signalUserData[signal]); unblockThread(m_mainThread); } - else { + else if (signal == kINTERRUPT || signal == kTERMINATE) { ARCH->cancelThread(m_mainThread); } unlockMutex(m_threadMutex); @@ -587,11 +592,9 @@ 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); + sigset_t sigset, oldsigset; + setSignalSet(&sigset); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); // fire up the INT and TERM signal handler thread. we could // instead arrange to catch and handle these signals but @@ -608,10 +611,7 @@ CArchMultithreadPosix::startSignalHandler() 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); + pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL); } } @@ -766,9 +766,7 @@ CArchMultithreadPosix::threadSignalHandler(void*) // add signal to mask sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); + setSignalSet(&sigset); // also wait on SIGABRT. on linux (others?) this thread (process) // will persist after all the other threads evaporate due to an @@ -791,6 +789,22 @@ CArchMultithreadPosix::threadSignalHandler(void*) #endif // if we get here then the signal was raised - ARCH->interrupt(); + switch (signal) { + case SIGINT: + ARCH->raiseSignal(kINTERRUPT); + break; + + case SIGTERM: + ARCH->raiseSignal(kTERMINATE); + break; + + case SIGHUP: + ARCH->raiseSignal(kHANGUP); + break; + + default: + // ignore + break; + } } } diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index 3921c8e9..2ac6183b 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -72,8 +72,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); - virtual void setInterruptHandler(InterruptFunc, void*); - virtual void interrupt(); + virtual void setSignalHandler(ESignal, SignalFunc, void*); + virtual void raiseSignal(ESignal); private: void startSignalHandler(); @@ -104,8 +104,8 @@ private: ThreadID m_nextID; pthread_t m_signalThread; - InterruptFunc m_signalFunc; - void* m_signalUserData; + SignalFunc m_signalFunc[kNUM_SIGNALS]; + void* m_signalUserData[kNUM_SIGNALS]; }; #endif diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index 384722c4..91ec38d4 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -80,13 +80,17 @@ CArchThreadImpl::~CArchThreadImpl() CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; -CArchMultithreadWindows::CArchMultithreadWindows() : - m_signalFunc(NULL), - m_signalUserData(NULL) +CArchMultithreadWindows::CArchMultithreadWindows() { assert(s_instance == NULL); s_instance = this; + // no signal handlers + for (size_t i = 0; i < kNUM_SIGNALS; ++i) { + m_signalFunc[i] = NULL; + m_signalUserData[i] = NULL; + } + // create mutex for thread list m_threadMutex = newMutex(); @@ -529,23 +533,24 @@ CArchMultithreadWindows::getIDOfThread(CArchThread thread) } void -CArchMultithreadWindows::setInterruptHandler(InterruptFunc func, void* userData) +CArchMultithreadWindows::setSignalHandler( + ESignal signal, SignalFunc func, void* userData) { lockMutex(m_threadMutex); - m_signalFunc = func; - m_signalUserData = userData; + m_signalFunc[signal] = func; + m_signalUserData[signal] = userData; unlockMutex(m_threadMutex); } void -CArchMultithreadWindows::interrupt() +CArchMultithreadWindows::raiseSignal(ESignal signal) { lockMutex(m_threadMutex); - if (m_signalFunc != NULL) { - m_signalFunc(m_signalUserData); + if (m_signalFunc[signal] != NULL) { + m_signalFunc[signal](signal, m_signalUserData[signal]); ARCH->unblockPollSocket(m_mainThread); } - else { + else if (signal == kINTERRUPT || signal == kTERMINATE) { ARCH->cancelThread(m_mainThread); } unlockMutex(m_threadMutex); diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index aecd4173..44a8d184 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -81,8 +81,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); - virtual void setInterruptHandler(InterruptFunc, void*); - virtual void interrupt(); + virtual void setSignalHandler(ESignal, SignalFunc, void*); + virtual void raiseSignal(ESignal); private: CArchThreadImpl* find(DWORD id); @@ -107,8 +107,8 @@ private: CThreadList m_threadList; CArchThread m_mainThread; - InterruptFunc m_signalFunc; - void* m_signalUserData; + SignalFunc m_signalFunc[kNUM_SIGNALS]; + void* m_signalUserData[kNUM_SIGNALS]; }; #endif diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index 477f42a7..d5eaa550 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -71,6 +71,19 @@ public: typedef void* (*ThreadFunc)(void*); //! Type of thread identifier typedef unsigned int ThreadID; + //! Types of signals + /*! + Not all platforms support all signals. Unsupported signals are + ignored. + */ + enum ESignal { + kINTERRUPT, //!< Interrupt (e.g. Ctrl+C) + kTERMINATE, //!< Terminate (e.g. Ctrl+Break) + kHANGUP, //!< Hangup (SIGHUP) + kNUM_SIGNALS + }; + //! Type of signal handler function + typedef void (*SignalFunc)(ESignal, void* userData); //! @name manipulators //@{ @@ -242,12 +255,16 @@ public: Sets the function to call on receipt of an external interrupt. By default and when \p func is NULL, the main thread is cancelled. */ - typedef void (*InterruptFunc)(void*); - virtual void setInterruptHandler(InterruptFunc func, + virtual void setSignalHandler(ESignal, SignalFunc func, void* userData) = 0; - //! Invoke the interrupt handler - virtual void interrupt() = 0; + //! Invoke the signal handler + /*! + Invokes the signal handler for \p signal, if any. If no handler + cancels the main thread for \c kINTERRUPT and \c kTERMINATE and + ignores the call otherwise. + */ + virtual void raiseSignal(ESignal signal) = 0; //@} }; diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index d3ff27bc..8e309ce3 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -1,4 +1,4 @@ -/* +;/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2004 Chris Schoeneman * @@ -22,7 +22,7 @@ // interrupt handler. this just adds a quit event to the queue. static void -interrupt(void*) +interrupt(CArch::ESignal, void*) { EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } @@ -37,14 +37,16 @@ CEventQueue::CEventQueue() : { setInstance(this); m_mutex = ARCH->newMutex(); - ARCH->setInterruptHandler(&interrupt, NULL); + ARCH->setSignalHandler(CArch::kINTERRUPT, &interrupt, NULL); + ARCH->setSignalHandler(CArch::kTERMINATE, &interrupt, NULL); m_buffer = new CSimpleEventQueueBuffer; } CEventQueue::~CEventQueue() { delete m_buffer; - ARCH->setInterruptHandler(NULL, NULL); + ARCH->setSignalHandler(CArch::kINTERRUPT, NULL, NULL); + ARCH->setSignalHandler(CArch::kTERMINATE, NULL, NULL); ARCH->closeMutex(m_mutex); setInstance(NULL); }