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
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]);

View File

@ -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;
}
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;
//@}
};

View File

@ -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);
}