Generalized signal handling. Now handling SIGHUP in addition

to SIGINT and SIGTERM.  Setup SIGHUP to reload the server's
configuration.
This commit is contained in:
crs 2004-02-28 17:49:29 +00:00
parent 9f7e909361
commit 82dffeb498
11 changed files with 183 additions and 88 deletions

View File

@ -358,6 +358,13 @@ static
int int
mainLoop() 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 // start the client. if this return false then we've failed and
// we shouldn't retry. // we shouldn't retry.
LOG((CLOG_DEBUG1 "starting client")); LOG((CLOG_DEBUG1 "starting client"));
@ -416,8 +423,6 @@ int
run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
{ {
// general initialization // general initialization
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
ARG->m_serverAddress = new CNetworkAddress; ARG->m_serverAddress = new CNetworkAddress;
ARG->m_pname = ARCH->getBasename(argv[0]); ARG->m_pname = ARCH->getBasename(argv[0]);

View File

@ -68,6 +68,7 @@
typedef int (*StartupFunc)(int, char**); typedef int (*StartupFunc)(int, char**);
static void parse(int argc, const char* const* argv); static void parse(int argc, const char* const* argv);
static bool loadConfig(const CString& pathname);
static void loadConfig(); static void loadConfig();
// //
@ -83,7 +84,7 @@ public:
m_backend(false), m_backend(false),
m_restartable(true), m_restartable(true),
m_daemon(true), m_daemon(true),
m_configFile(NULL), m_configFile(),
m_logFilter(NULL) m_logFilter(NULL)
{ s_instance = this; } { s_instance = this; }
~CArgs() { s_instance = NULL; } ~CArgs() { s_instance = NULL; }
@ -94,7 +95,7 @@ public:
bool m_backend; bool m_backend;
bool m_restartable; bool m_restartable;
bool m_daemon; bool m_daemon;
const char* m_configFile; CString m_configFile;
const char* m_logFilter; const char* m_logFilter;
CString m_name; CString m_name;
CNetworkAddress* m_synergyAddress; CNetworkAddress* m_synergyAddress;
@ -141,6 +142,7 @@ static CScreen* s_serverScreen = NULL;
static CPrimaryClient* s_primaryClient = NULL; static CPrimaryClient* s_primaryClient = NULL;
static CClientListener* s_listener = NULL; static CClientListener* s_listener = NULL;
static CServerTaskBarReceiver* s_taskBarReceiver = NULL; static CServerTaskBarReceiver* s_taskBarReceiver = NULL;
static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown;
static static
void void
@ -389,10 +391,38 @@ stopServer()
s_serverScreen = NULL; 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 static
int int
mainLoop() 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 // if configuration has no screens then add this system
// as the default // as the default
if (ARG->m_config->begin() == ARG->m_config->end()) { if (ARG->m_config->begin() == ARG->m_config->end()) {
@ -423,6 +453,13 @@ mainLoop()
return kExitFailed; 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 // run event loop. if startServer() failed we're supposed to retry
// later. the timer installed by startServer() will take care of // later. the timer installed by startServer() will take care of
// that. // that.
@ -438,6 +475,8 @@ mainLoop()
// close down // close down
LOG((CLOG_DEBUG1 "stopping server")); LOG((CLOG_DEBUG1 "stopping server"));
EVENTQUEUE->removeHandler(s_reloadConfigEvent,
IEventQueue::getSystemTarget());
stopServer(); stopServer();
updateStatus(); updateStatus();
LOG((CLOG_NOTE "stopped server")); LOG((CLOG_NOTE "stopped server"));
@ -477,8 +516,6 @@ int
run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
{ {
// general initialization // general initialization
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
ARG->m_synergyAddress = new CNetworkAddress; ARG->m_synergyAddress = new CNetworkAddress;
ARG->m_config = new CConfig; ARG->m_config = new CConfig;
ARG->m_pname = ARCH->getBasename(argv[0]); ARG->m_pname = ARCH->getBasename(argv[0]);
@ -759,14 +796,12 @@ parse(int argc, const char* const* argv)
static static
bool bool
loadConfig(const char* pathname) loadConfig(const CString& pathname)
{ {
assert(pathname != NULL);
try { try {
// load configuration // load configuration
LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname)); LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str()));
std::ifstream configStream(pathname); std::ifstream configStream(pathname.c_str());
if (!configStream) { if (!configStream) {
throw XConfigRead("cannot open file"); throw XConfigRead("cannot open file");
} }
@ -776,7 +811,7 @@ loadConfig(const char* pathname)
} }
catch (XConfigRead& e) { catch (XConfigRead& e) {
LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s",
pathname, e.what())); pathname.c_str(), e.what()));
} }
return false; return false;
} }
@ -788,7 +823,7 @@ loadConfig()
bool loaded = false; bool loaded = false;
// load the config file, if specified // load the config file, if specified
if (ARG->m_configFile != NULL) { if (!ARG->m_configFile.empty()) {
loaded = loadConfig(ARG->m_configFile); loaded = loadConfig(ARG->m_configFile);
} }
@ -801,14 +836,20 @@ loadConfig()
path = ARCH->concatPath(path, USR_CONFIG_NAME); path = ARCH->concatPath(path, USR_CONFIG_NAME);
// now try loading the user's configuration // now try loading the user's configuration
loaded = loadConfig(path.c_str()); if (loadConfig(path)) {
loaded = true;
ARG->m_configFile = path;
}
} }
if (!loaded) { if (!loaded) {
// try the system-wide config file // try the system-wide config file
path = ARCH->getSystemDirectory(); path = ARCH->getSystemDirectory();
if (!path.empty()) { if (!path.empty()) {
path = ARCH->concatPath(path, SYS_CONFIG_NAME); path = ARCH->concatPath(path, SYS_CONFIG_NAME);
loaded = loadConfig(path.c_str()); if (loadConfig(path)) {
loaded = true;
ARG->m_configFile = path;
}
} }
} }
} }

View File

@ -376,15 +376,15 @@ CArch::getIDOfThread(CArchThread thread)
} }
void 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 void
CArch::interrupt() CArch::raiseSignal(ESignal signal)
{ {
m_mt->interrupt(); m_mt->raiseSignal(signal);
} }
CArchSocket CArchSocket

View File

@ -120,8 +120,8 @@ public:
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);
virtual ThreadID getIDOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread);
virtual void setInterruptHandler(InterruptFunc, void*); virtual void setSignalHandler(ESignal, SignalFunc, void*);
virtual void interrupt(); virtual void raiseSignal(ESignal);
// IArchNetwork overrides // IArchNetwork overrides
virtual CArchSocket newSocket(EAddressFamily, ESocketType); virtual CArchSocket newSocket(EAddressFamily, ESocketType);

View File

@ -108,9 +108,20 @@ CArchConsoleWindows::getNewlineForConsole()
} }
BOOL WINAPI BOOL WINAPI
CArchConsoleWindows::signalHandler(DWORD) CArchConsoleWindows::signalHandler(DWORD ctrlType)
{ {
// terminate app and skip remaining handlers // terminate app and skip remaining handlers
ARCH->interrupt(); switch (ctrlType) {
case CTRL_C_EVENT:
ARCH->raiseSignal(CArch::kINTERRUPT);
return TRUE; return TRUE;
case CTRL_BREAK_EVENT:
ARCH->raiseSignal(CArch::kTERMINATE);
return TRUE;
default:
ARCH->raiseSignal(CArch::kINTERRUPT);
return TRUE;
}
} }

View File

@ -41,6 +41,16 @@
# define HAVE_POSIX_SIGWAIT 1 # define HAVE_POSIX_SIGWAIT 1
#endif #endif
static
void
setSignalSet(sigset_t* sigset)
{
sigemptyset(sigset);
sigaddset(sigset, SIGHUP);
sigaddset(sigset, SIGINT);
sigaddset(sigset, SIGTERM);
}
// //
// CArchThreadImpl // CArchThreadImpl
// //
@ -83,14 +93,18 @@ CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL;
CArchMultithreadPosix::CArchMultithreadPosix() : CArchMultithreadPosix::CArchMultithreadPosix() :
m_newThreadCalled(false), m_newThreadCalled(false),
m_nextID(0), m_nextID(0)
m_signalFunc(NULL),
m_signalUserData(NULL)
{ {
assert(s_instance == NULL); assert(s_instance == NULL);
s_instance = this; 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 // create mutex for thread list
m_threadMutex = newMutex(); m_threadMutex = newMutex();
@ -353,13 +367,6 @@ CArchMultithreadPosix::newThread(ThreadFunc func, void* data)
thread->m_func = func; thread->m_func = func;
thread->m_userData = data; 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 // create the thread. pthread_create() on RedHat 7.2 smp fails
// if passed a NULL attr so use a default attr. // if passed a NULL attr so use a default attr.
pthread_attr_t attr; pthread_attr_t attr;
@ -370,9 +377,6 @@ CArchMultithreadPosix::newThread(ThreadFunc func, void* data)
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
} }
// restore signals
pthread_sigmask(SIG_SETMASK, &oldsigset, NULL);
// check if thread was started // check if thread was started
if (status != 0) { if (status != 0) {
// failed to start thread so clean up // failed to start thread so clean up
@ -560,23 +564,24 @@ CArchMultithreadPosix::getIDOfThread(CArchThread thread)
} }
void void
CArchMultithreadPosix::setInterruptHandler(InterruptFunc func, void* userData) CArchMultithreadPosix::setSignalHandler(
ESignal signal, SignalFunc func, void* userData)
{ {
lockMutex(m_threadMutex); lockMutex(m_threadMutex);
m_signalFunc = func; m_signalFunc[signal] = func;
m_signalUserData = userData; m_signalUserData[signal] = userData;
unlockMutex(m_threadMutex); unlockMutex(m_threadMutex);
} }
void void
CArchMultithreadPosix::interrupt() CArchMultithreadPosix::raiseSignal(ESignal signal)
{ {
lockMutex(m_threadMutex); lockMutex(m_threadMutex);
if (m_signalFunc != NULL) { if (m_signalFunc[signal] != NULL) {
m_signalFunc(m_signalUserData); m_signalFunc[signal](signal, m_signalUserData[signal]);
unblockThread(m_mainThread); unblockThread(m_mainThread);
} }
else { else if (signal == kINTERRUPT || signal == kTERMINATE) {
ARCH->cancelThread(m_mainThread); ARCH->cancelThread(m_mainThread);
} }
unlockMutex(m_threadMutex); unlockMutex(m_threadMutex);
@ -587,11 +592,9 @@ CArchMultithreadPosix::startSignalHandler()
{ {
// set signal mask. the main thread blocks these signals and // set signal mask. the main thread blocks these signals and
// the signal handler thread will listen for them. // the signal handler thread will listen for them.
sigset_t sigset; sigset_t sigset, oldsigset;
sigemptyset(&sigset); setSignalSet(&sigset);
sigaddset(&sigset, SIGINT); pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset);
sigaddset(&sigset, SIGTERM);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
// fire up the INT and TERM signal handler thread. we could // fire up the INT and TERM signal handler thread. we could
// instead arrange to catch and handle these signals but // instead arrange to catch and handle these signals but
@ -608,10 +611,7 @@ CArchMultithreadPosix::startSignalHandler()
if (status != 0) { if (status != 0) {
// can't create thread to wait for signal so don't block // can't create thread to wait for signal so don't block
// the signals. // the signals.
sigemptyset(&sigset); pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
} }
} }
@ -766,9 +766,7 @@ CArchMultithreadPosix::threadSignalHandler(void*)
// add signal to mask // add signal to mask
sigset_t sigset; sigset_t sigset;
sigemptyset(&sigset); setSignalSet(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
// also wait on SIGABRT. on linux (others?) this thread (process) // also wait on SIGABRT. on linux (others?) this thread (process)
// will persist after all the other threads evaporate due to an // will persist after all the other threads evaporate due to an
@ -791,6 +789,22 @@ CArchMultithreadPosix::threadSignalHandler(void*)
#endif #endif
// if we get here then the signal was raised // 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;
}
} }
} }

View File

@ -72,8 +72,8 @@ public:
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);
virtual ThreadID getIDOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread);
virtual void setInterruptHandler(InterruptFunc, void*); virtual void setSignalHandler(ESignal, SignalFunc, void*);
virtual void interrupt(); virtual void raiseSignal(ESignal);
private: private:
void startSignalHandler(); void startSignalHandler();
@ -104,8 +104,8 @@ private:
ThreadID m_nextID; ThreadID m_nextID;
pthread_t m_signalThread; pthread_t m_signalThread;
InterruptFunc m_signalFunc; SignalFunc m_signalFunc[kNUM_SIGNALS];
void* m_signalUserData; void* m_signalUserData[kNUM_SIGNALS];
}; };
#endif #endif

View File

@ -80,13 +80,17 @@ CArchThreadImpl::~CArchThreadImpl()
CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL;
CArchMultithreadWindows::CArchMultithreadWindows() : CArchMultithreadWindows::CArchMultithreadWindows()
m_signalFunc(NULL),
m_signalUserData(NULL)
{ {
assert(s_instance == NULL); assert(s_instance == NULL);
s_instance = this; 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 // create mutex for thread list
m_threadMutex = newMutex(); m_threadMutex = newMutex();
@ -529,23 +533,24 @@ CArchMultithreadWindows::getIDOfThread(CArchThread thread)
} }
void void
CArchMultithreadWindows::setInterruptHandler(InterruptFunc func, void* userData) CArchMultithreadWindows::setSignalHandler(
ESignal signal, SignalFunc func, void* userData)
{ {
lockMutex(m_threadMutex); lockMutex(m_threadMutex);
m_signalFunc = func; m_signalFunc[signal] = func;
m_signalUserData = userData; m_signalUserData[signal] = userData;
unlockMutex(m_threadMutex); unlockMutex(m_threadMutex);
} }
void void
CArchMultithreadWindows::interrupt() CArchMultithreadWindows::raiseSignal(ESignal signal)
{ {
lockMutex(m_threadMutex); lockMutex(m_threadMutex);
if (m_signalFunc != NULL) { if (m_signalFunc[signal] != NULL) {
m_signalFunc(m_signalUserData); m_signalFunc[signal](signal, m_signalUserData[signal]);
ARCH->unblockPollSocket(m_mainThread); ARCH->unblockPollSocket(m_mainThread);
} }
else { else if (signal == kINTERRUPT || signal == kTERMINATE) {
ARCH->cancelThread(m_mainThread); ARCH->cancelThread(m_mainThread);
} }
unlockMutex(m_threadMutex); unlockMutex(m_threadMutex);

View File

@ -81,8 +81,8 @@ public:
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);
virtual ThreadID getIDOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread);
virtual void setInterruptHandler(InterruptFunc, void*); virtual void setSignalHandler(ESignal, SignalFunc, void*);
virtual void interrupt(); virtual void raiseSignal(ESignal);
private: private:
CArchThreadImpl* find(DWORD id); CArchThreadImpl* find(DWORD id);
@ -107,8 +107,8 @@ private:
CThreadList m_threadList; CThreadList m_threadList;
CArchThread m_mainThread; CArchThread m_mainThread;
InterruptFunc m_signalFunc; SignalFunc m_signalFunc[kNUM_SIGNALS];
void* m_signalUserData; void* m_signalUserData[kNUM_SIGNALS];
}; };
#endif #endif

View File

@ -71,6 +71,19 @@ public:
typedef void* (*ThreadFunc)(void*); typedef void* (*ThreadFunc)(void*);
//! Type of thread identifier //! Type of thread identifier
typedef unsigned int ThreadID; 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 //! @name manipulators
//@{ //@{
@ -242,12 +255,16 @@ public:
Sets the function to call on receipt of an external interrupt. Sets the function to call on receipt of an external interrupt.
By default and when \p func is NULL, the main thread is cancelled. By default and when \p func is NULL, the main thread is cancelled.
*/ */
typedef void (*InterruptFunc)(void*); virtual void setSignalHandler(ESignal, SignalFunc func,
virtual void setInterruptHandler(InterruptFunc func,
void* userData) = 0; void* userData) = 0;
//! Invoke the interrupt handler //! Invoke the signal handler
virtual void interrupt() = 0; /*!
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;
//@} //@}
}; };

View File

@ -1,4 +1,4 @@
/* ;/*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman * Copyright (C) 2004 Chris Schoeneman
* *
@ -22,7 +22,7 @@
// interrupt handler. this just adds a quit event to the queue. // interrupt handler. this just adds a quit event to the queue.
static static
void void
interrupt(void*) interrupt(CArch::ESignal, void*)
{ {
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
} }
@ -37,14 +37,16 @@ CEventQueue::CEventQueue() :
{ {
setInstance(this); setInstance(this);
m_mutex = ARCH->newMutex(); m_mutex = ARCH->newMutex();
ARCH->setInterruptHandler(&interrupt, NULL); ARCH->setSignalHandler(CArch::kINTERRUPT, &interrupt, NULL);
ARCH->setSignalHandler(CArch::kTERMINATE, &interrupt, NULL);
m_buffer = new CSimpleEventQueueBuffer; m_buffer = new CSimpleEventQueueBuffer;
} }
CEventQueue::~CEventQueue() CEventQueue::~CEventQueue()
{ {
delete m_buffer; delete m_buffer;
ARCH->setInterruptHandler(NULL, NULL); ARCH->setSignalHandler(CArch::kINTERRUPT, NULL, NULL);
ARCH->setSignalHandler(CArch::kTERMINATE, NULL, NULL);
ARCH->closeMutex(m_mutex); ARCH->closeMutex(m_mutex);
setInstance(NULL); setInstance(NULL);
} }