performance fixes on win32 plus clean up of some warnings. also

improved error messages when uninstalling service.
This commit is contained in:
crs 2002-06-14 18:08:20 +00:00
parent 21af7b2f17
commit e3dcf7febf
21 changed files with 340 additions and 177 deletions

View File

@ -11,6 +11,11 @@ CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) :
// do nothing // do nothing
} }
CFunctionJob::~CFunctionJob()
{
// do nothing
}
void void
CFunctionJob::run() CFunctionJob::run()
{ {

View File

@ -6,6 +6,7 @@
class CFunctionJob : public IJob { class CFunctionJob : public IJob {
public: public:
CFunctionJob(void (*func)(void*), void* arg = NULL); CFunctionJob(void (*func)(void*), void* arg = NULL);
virtual ~CFunctionJob();
// IJob overrides // IJob overrides
virtual void run(); virtual void run();

View File

@ -65,6 +65,11 @@ CLog::print(
fmt += 3; fmt += 3;
} }
// done if below priority threshold
if (priority > getMaxPriority()) {
return;
}
// compute prefix padding length // compute prefix padding length
int pad = g_priorityPad; int pad = g_priorityPad;
@ -98,6 +103,11 @@ CLog::printt(
fmt += 3; fmt += 3;
} }
// done if below priority threshold
if (priority > getMaxPriority()) {
return;
}
// compute prefix padding length // compute prefix padding length
char stack[1024]; char stack[1024];
sprintf(stack, "%d", line); sprintf(stack, "%d", line);
@ -217,34 +227,32 @@ CLog::output(
char* msg) char* msg)
{ {
assert(priority >= -1 && priority < g_numPriority); assert(priority >= -1 && priority < g_numPriority);
assert(msg != 0); assert(msg != NULL);
if (priority <= getMaxPriority()) { // insert priority label
// insert priority label int n = -g_prioritySuffixLength;
int n = -g_prioritySuffixLength; if (priority >= 0) {
if (priority >= 0) { n = strlen(g_priority[priority]);
n = strlen(g_priority[priority]); sprintf(msg + g_maxPriorityLength - n,
sprintf(msg + g_maxPriorityLength - n, "%s:", g_priority[priority]);
"%s:", g_priority[priority]); msg[g_maxPriorityLength + 1] = ' ';
msg[g_maxPriorityLength + 1] = ' '; }
}
// put a newline at the end // put a newline at the end
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
strcat(msg + g_priorityPad, "\r\n"); strcat(msg + g_priorityPad, "\r\n");
#else #else
strcat(msg + g_priorityPad, "\n"); strcat(msg + g_priorityPad, "\n");
#endif #endif
// print it // print it
CHoldLock lock(s_lock); CHoldLock lock(s_lock);
if (s_outputter == NULL || if (s_outputter == NULL ||
!s_outputter(priority, msg + g_maxPriorityLength - n)) { !s_outputter(priority, msg + g_maxPriorityLength - n)) {
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
openConsole(); openConsole();
#endif #endif
fprintf(stderr, "%s", msg + g_maxPriorityLength - n); fprintf(stderr, "%s", msg + g_maxPriorityLength - n);
}
} }
} }
@ -267,7 +275,7 @@ CLog::vsprint(
} }
// start allocating buffers until we write the whole string // start allocating buffers until we write the whole string
buffer = 0; buffer = NULL;
do { do {
delete[] buffer; delete[] buffer;
len *= 2; len *= 2;

View File

@ -7,6 +7,7 @@ template <class T>
class TMethodJob : public IJob { class TMethodJob : public IJob {
public: public:
TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL);
virtual ~TMethodJob();
// IJob overrides // IJob overrides
virtual void run(); virtual void run();
@ -27,6 +28,13 @@ TMethodJob<T>::TMethodJob(T* object, void (T::*method)(void*), void* arg) :
// do nothing // do nothing
} }
template <class T>
inline
TMethodJob<T>::~TMethodJob()
{
// do nothing
}
template <class T> template <class T>
inline inline
void void

View File

@ -30,6 +30,12 @@
// this one's a little too aggressive // this one's a little too aggressive
#pragma warning(disable: 4127) // conditional expression is constant #pragma warning(disable: 4127) // conditional expression is constant
// emitted incorrectly under release build in some circumstances
#if defined(NDEBUG)
#pragma warning(disable: 4702) // unreachable code
#pragma warning(disable: 4701) // local variable maybe used uninitialized
#endif
#endif // (_MSC_VER >= 1200) #endif // (_MSC_VER >= 1200)
#else #else

View File

@ -16,6 +16,7 @@
CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() :
m_client(NULL), m_client(NULL),
m_threadID(0), m_threadID(0),
m_lastThreadID(0),
m_desk(NULL), m_desk(NULL),
m_deskName(), m_deskName(),
m_window(NULL), m_window(NULL),
@ -741,17 +742,23 @@ CMSWindowsSecondaryScreen::syncDesktop() const
{ {
// note -- mutex must be locked on entry // note -- mutex must be locked on entry
DWORD threadID = GetCurrentThreadId(); // change calling thread's desktop
if (!m_is95Family) { if (!m_is95Family) {
if (GetThreadDesktop(threadID) != m_desk) { if (SetThreadDesktop(m_desk) == 0) {
// FIXME -- this doesn't work. if we set a desktop then log((CLOG_WARN "failed to set desktop: %d", GetLastError()));
// sending events doesn't work.
if (SetThreadDesktop(m_desk) == 0) {
log((CLOG_ERR "failed to set desktop: %d", GetLastError()));
}
} }
} }
AttachThreadInput(threadID, m_threadID, TRUE);
// attach input queues if not already attached. this has a habit
// of sucking up more and more CPU each time it's called (even if
// the threads are already attached). since we only expect one
// thread to call this more than once we can save just the last
// the attached thread.
DWORD threadID = GetCurrentThreadId();
if (threadID != m_lastThreadID && threadID != m_threadID) {
m_lastThreadID = threadID;
AttachThreadInput(threadID, m_threadID, TRUE);
}
} }
CString CString

View File

@ -89,6 +89,9 @@ private:
// the main loop's thread id // the main loop's thread id
DWORD m_threadID; DWORD m_threadID;
// the thread id of the last attached thread
mutable DWORD m_lastThreadID;
// the current desk and it's name // the current desk and it's name
HDESK m_desk; HDESK m_desk;
CString m_deskName; CString m_deskName;

View File

@ -16,10 +16,8 @@
// platform dependent name of a daemon // platform dependent name of a daemon
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
#define DAEMON "service"
#define DAEMON_NAME "Synergy Client" #define DAEMON_NAME "Synergy Client"
#elif defined(CONFIG_PLATFORM_UNIX) #elif defined(CONFIG_PLATFORM_UNIX)
#define DAEMON "daemon"
#define DAEMON_NAME "synergy" #define DAEMON_NAME "synergy"
#endif #endif
@ -127,8 +125,6 @@ realMain(
// terminated // terminated
return 1; return 1;
} }
return 0;
} }
static static
@ -182,17 +178,34 @@ static
void void
help() help()
{ {
#if defined(CONFIG_PLATFORM_WIN32)
# define PLATFORM_ARGS \
" [--install]" \
" <server-address>\n" \
"or\n" \
" --uninstall\n"
# define PLATFORM_DESC \
" --install install server as a service.\n" \
" --uninstall uninstall server service.\n"
#else
# define PLATFORM_ARGS \
" <server-address>\n"
# define PLATFORM_DESC
#endif
log((CLOG_PRINT log((CLOG_PRINT
"Usage: %s" "Usage: %s"
" [--"DAEMON"|--no-"DAEMON"]"
" [--camp|--no-camp]" " [--camp|--no-camp]"
" [--daemon|--no-daemon]"
" [--debug <level>]" " [--debug <level>]"
" [--name <screen-name>]" " [--name <screen-name>]"
" [--restart|--no-restart]" " [--restart|--no-restart]"
" [--install]" PLATFORM_ARGS
" <server-address>\n" "\n"
"or\n"
" --uninstall\n"
"Start the synergy mouse/keyboard sharing server.\n" "Start the synergy mouse/keyboard sharing server.\n"
"\n" "\n"
"* --camp keep attempting to connect to the server until\n" "* --camp keep attempting to connect to the server until\n"
@ -201,8 +214,8 @@ help()
" -d, --debug <level> filter out log messages with priorty below level.\n" " -d, --debug <level> filter out log messages with priorty below level.\n"
" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
" DEBUG, DEBUG1, DEBUG2.\n" " DEBUG, DEBUG1, DEBUG2.\n"
" -f, --no-"DAEMON" run the client in the foreground.\n" " -f, --no-daemon run the client in the foreground.\n"
"* --"DAEMON" run the client as a "DAEMON".\n" "* --daemon run the client as a daemon.\n"
" -n, --name <screen-name> use screen-name instead the hostname to identify\n" " -n, --name <screen-name> use screen-name instead the hostname to identify\n"
" ourself to the server.\n" " ourself to the server.\n"
" -1, --no-restart do not try to restart the client if it fails for\n" " -1, --no-restart do not try to restart the client if it fails for\n"
@ -210,9 +223,8 @@ help()
"* --restart restart the client automatically if it fails for\n" "* --restart restart the client automatically if it fails for\n"
" some unexpected reason, including the server\n" " some unexpected reason, including the server\n"
" disconnecting but not including failing to\n" " disconnecting but not including failing to\n"
" connect to the server." " connect to the server.\n"
" --install install server as a "DAEMON".\n" PLATFORM_DESC
" --uninstall uninstall server "DAEMON".\n"
" -h, --help display this help and exit.\n" " -h, --help display this help and exit.\n"
" --version display version information and exit.\n" " --version display version information and exit.\n"
"\n" "\n"
@ -223,7 +235,7 @@ help()
"default port, %d.\n" "default port, %d.\n"
"\n" "\n"
"Where log messages go depends on the platform and whether or not the\n" "Where log messages go depends on the platform and whether or not the\n"
"client is running as a "DAEMON".", "client is running as a daemon.",
pname, kDefaultPort)); pname, kDefaultPort));
} }
@ -291,12 +303,12 @@ parse(
s_camp = false; s_camp = false;
} }
else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
// not a daemon // not a daemon
s_daemon = false; s_daemon = false;
} }
else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { else if (isArg(i, argc, argv, NULL, "--daemon")) {
// daemonize // daemonize
s_daemon = true; s_daemon = true;
} }
@ -321,12 +333,8 @@ parse(
bye(0); bye(0);
} }
#if defined(CONFIG_PLATFORM_WIN32)
else if (isArg(i, argc, argv, NULL, "--install")) { else if (isArg(i, argc, argv, NULL, "--install")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_install = true; s_install = true;
if (s_uninstall) { if (s_uninstall) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'" log((CLOG_PRINT "%s: `--install' and `--uninstall'"
@ -335,13 +343,10 @@ parse(
bye(2); bye(2);
} }
} }
else if (isArg(i, argc, argv, NULL, "--uninstall")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif #endif
#if defined(CONFIG_PLATFORM_WIN32)
else if (isArg(i, argc, argv, NULL, "--uninstall")) {
s_uninstall = true; s_uninstall = true;
if (s_install) { if (s_install) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'" log((CLOG_PRINT "%s: `--install' and `--uninstall'"
@ -350,6 +355,7 @@ parse(
bye(2); bye(2);
} }
} }
#endif
else if (isArg(i, argc, argv, "--", NULL)) { else if (isArg(i, argc, argv, "--", NULL)) {
// remaining arguments are not options // remaining arguments are not options
@ -490,16 +496,6 @@ daemonStartup(
return platform->runDaemon(realMain, daemonStop); return platform->runDaemon(realMain, daemonStop);
} }
static
int
daemonStartup95(
IPlatform*,
int,
const char**)
{
return realMain(NULL);
}
static static
bool bool
logDiscard( logDiscard(
@ -556,12 +552,13 @@ WinMain(
// ignore // ignore
} }
// send PRINT and FATAL output to a message box
CLog::setOutputter(&logMessageBox);
// if we're not starting as an NT service then reparse the command // if we're not starting as an NT service then reparse the command
// line normally. // line normally.
if (s_die || !s_daemon || s_install || s_uninstall || if (s_die || __argc > 1 || s_install || s_uninstall ||
CWin32Platform::isWindows95Family()) { CWin32Platform::isWindows95Family()) {
// send PRINT and FATAL output to a message box
CLog::setOutputter(&logMessageBox);
// exit on bye // exit on bye
bye = &exit; bye = &exit;
@ -570,9 +567,9 @@ WinMain(
parse(__argc, const_cast<const char**>(__argv)); parse(__argc, const_cast<const char**>(__argv));
} }
// if starting as a daemon then we ignore the startup command line // if no arguments were provided then we're hopefully starting as
// here. we'll parse the command line passed in when the service // a service. we'll parse the command line passed in when the
// control manager calls us back. // service control manager calls us back.
else { else {
// do nothing // do nothing
} }
@ -588,7 +585,7 @@ WinMain(
} }
// construct the command line to start the service with // construct the command line to start the service with
CString commandLine = "--"DAEMON; CString commandLine = "--daemon";
if (s_restartable) { if (s_restartable) {
commandLine += " --restart"; commandLine += " --restart";
} }
@ -613,29 +610,42 @@ WinMain(
return 0; return 0;
} }
else if (s_uninstall) { else if (s_uninstall) {
if (!platform.uninstallDaemon(DAEMON_NAME)) { switch (platform.uninstallDaemon(DAEMON_NAME)) {
case IPlatform::kSuccess:
log((CLOG_PRINT "uninstalled successfully"));
return 0;
case IPlatform::kFailed:
log((CLOG_CRIT "failed to uninstall service")); log((CLOG_CRIT "failed to uninstall service"));
return 16; return 16;
case IPlatform::kAlready:
log((CLOG_CRIT "service isn't installed"));
return 16;
} }
log((CLOG_PRINT "uninstalled successfully"));
return 0;
} }
// daemonize if requested // daemonize if requested
int result; int result;
if (s_daemon) { if (__argc <= 1) {
if (CWin32Platform::isWindows95Family()) { if (CWin32Platform::isWindows95Family()) {
result = platform.daemonize(DAEMON_NAME, &daemonStartup95); result = -1;
} }
else { else {
result = platform.daemonize(DAEMON_NAME, &daemonStartup); result = platform.daemonize(DAEMON_NAME, &daemonStartup);
} }
if (result == -1) { if (result == -1) {
log((CLOG_CRIT "failed to start as a service")); log((CLOG_CRIT "failed to start as a service" BYE, pname));
return 16; return 16;
} }
} }
else { else {
// if a daemon then redirect log
if (s_daemon) {
platform.installDaemonLogger(__argv[0]);
}
// run
result = restartableMain(); result = restartableMain();
} }

View File

@ -7,7 +7,8 @@
const UInt32 CStreamBuffer::kChunkSize = 4096; const UInt32 CStreamBuffer::kChunkSize = 4096;
CStreamBuffer::CStreamBuffer() : CStreamBuffer::CStreamBuffer() :
m_size(0) m_size(0),
m_headUsed(0)
{ {
// do nothing // do nothing
} }
@ -25,17 +26,17 @@ CStreamBuffer::peek(
// reserve space in first chunk // reserve space in first chunk
ChunkList::iterator head = m_chunks.begin(); ChunkList::iterator head = m_chunks.begin();
head->reserve(n); head->reserve(n + m_headUsed);
// consolidate chunks into the first chunk until it has n bytes // consolidate chunks into the first chunk until it has n bytes
ChunkList::iterator scan = head; ChunkList::iterator scan = head;
++scan; ++scan;
while (head->size() < n && scan != m_chunks.end()) { while (head->size() - m_headUsed < n && scan != m_chunks.end()) {
head->insert(head->end(), scan->begin(), scan->end()); head->insert(head->end(), scan->begin(), scan->end());
scan = m_chunks.erase(scan); scan = m_chunks.erase(scan);
} }
return reinterpret_cast<const void*>(head->begin()); return reinterpret_cast<const void*>(head->begin() + m_headUsed);
} }
void void
@ -44,7 +45,8 @@ CStreamBuffer::pop(
{ {
// discard all chunks if n is greater than or equal to m_size // discard all chunks if n is greater than or equal to m_size
if (n >= m_size) { if (n >= m_size) {
m_size = 0; m_size = 0;
m_headUsed = 0;
m_chunks.clear(); m_chunks.clear();
return; return;
} }
@ -55,15 +57,16 @@ CStreamBuffer::pop(
// discard chunks until more than n bytes would've been discarded // discard chunks until more than n bytes would've been discarded
ChunkList::iterator scan = m_chunks.begin(); ChunkList::iterator scan = m_chunks.begin();
assert(scan != m_chunks.end()); assert(scan != m_chunks.end());
while (scan->size() <= n) { while (scan->size() - m_headUsed <= n) {
n -= scan->size(); n -= scan->size() - m_headUsed;
scan = m_chunks.erase(scan); m_headUsed = 0;
scan = m_chunks.erase(scan);
assert(scan != m_chunks.end()); assert(scan != m_chunks.end());
} }
// remove left over bytes from the head chunk // remove left over bytes from the head chunk
if (n > 0) { if (n > 0) {
scan->erase(scan->begin(), scan->begin() + n); m_headUsed += n;
} }
} }

View File

@ -33,6 +33,7 @@ private:
ChunkList m_chunks; ChunkList m_chunks;
UInt32 m_size; UInt32 m_size;
UInt32 m_headUsed;
}; };
#endif #endif

View File

@ -105,6 +105,16 @@ CThread::wait(
return currentRep->wait(m_rep, timeout); return currentRep->wait(m_rep, timeout);
} }
#if defined(CONFIG_PLATFORM_WIN32)
bool
CThread::waitForEvent(
double timeout)
{
CThreadPtr currentRep(CThreadRep::getCurrentThreadRep());
return currentRep->waitForEvent(timeout);
}
#endif
void void
CThread::testCancel() CThread::testCancel()
{ {

View File

@ -1,6 +1,8 @@
#ifndef CTHREAD_H #ifndef CTHREAD_H
#define CTHREAD_H #define CTHREAD_H
#include "common.h"
class IJob; class IJob;
class CThreadRep; class CThreadRep;
@ -103,6 +105,13 @@ public:
// (cancellation point) // (cancellation point)
bool wait(double timeout = -1.0) const; bool wait(double timeout = -1.0) const;
#if defined(CONFIG_PLATFORM_WIN32)
// wait for a message in the queue. returns true if a message
// is available.
// (cancellation point)
static bool waitForEvent(double timeout = -1.0);
#endif
// get the exit result. does an implicit wait(). returns NULL // get the exit result. does an implicit wait(). returns NULL
// immediately if called by a thread on itself. returns NULL for // immediately if called by a thread on itself. returns NULL for
// threads that were cancelled. // threads that were cancelled.

View File

@ -614,7 +614,45 @@ CThreadRep::wait(
testCancel(); testCancel();
default: default:
// error // timeout or error
return false;
}
}
bool
CThreadRep::waitForEvent(
double timeout)
{
// 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; return false;
} }
} }

View File

@ -43,6 +43,11 @@ public:
// wait for thread to exit or for current thread to cancel // wait for thread to exit or for current thread to cancel
bool wait(CThreadRep*, double timeout); bool wait(CThreadRep*, double timeout);
#if defined(CONFIG_PLATFORM_WIN32)
// wait for a message on the queue
bool waitForEvent(double timeout);
#endif
// set the priority // set the priority
void setPriority(int n); void setPriority(int n);

View File

@ -202,9 +202,7 @@ CMSWindowsScreen::getEvent(
MSG* msg) const MSG* msg) const
{ {
// wait for an event in a cancellable way // wait for an event in a cancellable way
while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) { CThread::waitForEvent();
CThread::sleep(0.01);
}
GetMessage(msg, NULL, 0, 0); GetMessage(msg, NULL, 0, 0);
} }

View File

@ -35,12 +35,12 @@ CUnixPlatform::installDaemon(
return true; return true;
} }
bool CUnixPlatform::EResult
CUnixPlatform::uninstallDaemon( CUnixPlatform::uninstallDaemon(
const char*) const char*)
{ {
// daemons don't require special installation // daemons don't require special installation
return true; return kSuccess;
} }
int int
@ -85,12 +85,20 @@ CUnixPlatform::daemonize(
dup(1); dup(1);
// hook up logger // hook up logger
setDaemonLogger(name); installDaemonLogger(name);
// invoke function // invoke function
return func(this, 1, &name); return func(this, 1, &name);
} }
void
CUnixPlatform::installDaemonLogger(
const char* name)
{
openlog(name, 0, LOG_DAEMON);
CLog::setOutputter(&CUnixPlatform::deamonLogger);
}
int int
CUnixPlatform::restart( CUnixPlatform::restart(
RestartFunc func, RestartFunc func,
@ -195,14 +203,6 @@ CUnixPlatform::addPathComponent(
return path; return path;
} }
void
CUnixPlatform::setDaemonLogger(
const char* name)
{
openlog(name, 0, LOG_DAEMON);
CLog::setOutputter(&CUnixPlatform::deamonLogger);
}
bool bool
CUnixPlatform::deamonLogger( CUnixPlatform::deamonLogger(
int priority, int priority,

View File

@ -13,8 +13,9 @@ public:
const char* description, const char* description,
const char* pathname, const char* pathname,
const char* commandLine); const char* commandLine);
virtual bool uninstallDaemon(const char* name); virtual EResult uninstallDaemon(const char* name);
virtual int daemonize(const char* name, DaemonFunc); virtual int daemonize(const char* name, DaemonFunc);
virtual void installDaemonLogger(const char* name);
virtual int restart(RestartFunc, int minErrorCode); virtual int restart(RestartFunc, int minErrorCode);
virtual const char* getBasename(const char* pathname) const; virtual const char* getBasename(const char* pathname) const;
virtual CString getUserDirectory() const; virtual CString getUserDirectory() const;
@ -23,9 +24,6 @@ public:
const CString& prefix, const CString& prefix,
const CString& suffix) const; const CString& suffix) const;
protected:
virtual void setDaemonLogger(const char* name);
private: private:
static bool deamonLogger(int, const char*); static bool deamonLogger(int, const char*);
}; };

View File

@ -180,7 +180,7 @@ CWin32Platform::installDaemon(
} }
} }
bool IPlatform::EResult
CWin32Platform::uninstallDaemon( CWin32Platform::uninstallDaemon(
const char* name) const char* name)
{ {
@ -190,7 +190,7 @@ CWin32Platform::uninstallDaemon(
HKEY key = open95ServicesKey(); HKEY key = open95ServicesKey();
if (key == NULL) { if (key == NULL) {
log((CLOG_ERR "cannot open RunServices registry key", GetLastError())); log((CLOG_ERR "cannot open RunServices registry key", GetLastError()));
return false; return kAlready;
} }
// remove entry // remove entry
@ -199,7 +199,7 @@ CWin32Platform::uninstallDaemon(
// clean up // clean up
closeKey(key); closeKey(key);
return true; return kSuccess;
} }
// windows NT family services // windows NT family services
@ -216,26 +216,41 @@ CWin32Platform::uninstallDaemon(
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
if (mgr == NULL) { if (mgr == NULL) {
log((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); log((CLOG_ERR "OpenSCManager failed with %d", GetLastError()));
return false; return kFailed;
} }
// open the service. oddly, you must open a service to delete it. // open the service. oddly, you must open a service to delete it.
bool success; EResult result;
SC_HANDLE service = OpenService(mgr, name, DELETE); SC_HANDLE service = OpenService(mgr, name, DELETE);
if (service == NULL) { if (service == NULL) {
log((CLOG_ERR "OpenService failed with %d", GetLastError())); const DWORD e = GetLastError();
success = false; log((CLOG_ERR "OpenService failed with %d", e));
result = (e == ERROR_SERVICE_DOES_NOT_EXIST) ? kAlready : kFailed;
} }
else { else {
success = (DeleteService(service) != 0); if (DeleteService(service) != 0) {
result = kSuccess;
}
else {
const DWORD e = GetLastError();
switch (e) {
case ERROR_SERVICE_MARKED_FOR_DELETE:
result = kAlready;
break;
default:
result = kFailed;
break;
}
}
CloseServiceHandle(service); CloseServiceHandle(service);
} }
// close the manager // close the manager
CloseServiceHandle(mgr); CloseServiceHandle(mgr);
return success; return result;
} }
} }
@ -303,6 +318,21 @@ CWin32Platform::daemonize(
} }
} }
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);
}
}
}
}
int int
CWin32Platform::restart( CWin32Platform::restart(
RestartFunc func, RestartFunc func,
@ -643,12 +673,7 @@ CWin32Platform::serviceMain(
const char** argv = const_cast<const char**>(argvIn); const char** argv = const_cast<const char**>(argvIn);
// open event log and direct log messages to it // open event log and direct log messages to it
if (s_eventLog == NULL) { installDaemonLogger(argv[0]);
s_eventLog = RegisterEventSource(NULL, argv[0]);
if (s_eventLog != NULL) {
CLog::setOutputter(&CWin32Platform::serviceLogger);
}
}
// create synchronization objects // create synchronization objects
CThread::init(); CThread::init();

View File

@ -46,8 +46,9 @@ public:
const char* description, const char* description,
const char* pathname, const char* pathname,
const char* commandLine); const char* commandLine);
virtual bool uninstallDaemon(const char* name); virtual EResult uninstallDaemon(const char* name);
virtual int daemonize(const char* name, DaemonFunc); virtual int daemonize(const char* name, DaemonFunc);
virtual void installDaemonLogger(const char* name);
virtual int restart(RestartFunc, int minErrorCode); virtual int restart(RestartFunc, int minErrorCode);
virtual const char* getBasename(const char* pathname) const; virtual const char* getBasename(const char* pathname) const;
virtual CString getUserDirectory() const; virtual CString getUserDirectory() const;

View File

@ -9,6 +9,12 @@ public:
typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv);
typedef int (*RestartFunc)(); typedef int (*RestartFunc)();
enum EResult {
kSuccess,
kFailed,
kAlready
};
// manipulators // manipulators
// install/uninstall a daemon. commandLine should *not* // install/uninstall a daemon. commandLine should *not*
@ -18,12 +24,10 @@ public:
const char* description, const char* description,
const char* pathname, const char* pathname,
const char* commandLine) = 0; const char* commandLine) = 0;
virtual bool uninstallDaemon(const char* name) = 0; virtual EResult uninstallDaemon(const char* name) = 0;
// daemonize. this should have the side effect of sending log // daemonize. this should call installDaemonLogger(). returns
// messages to a system message logger since messages can no // true iff successful. the name is the name of the daemon.
// longer go to the console. returns true iff successful.
// the name is the name of the daemon.
// daemonize. this should have the side effect of sending log // daemonize. this should have the side effect of sending log
// messages to a system message logger since messages can no // messages to a system message logger since messages can no
@ -44,6 +48,11 @@ public:
// SetServiceStatus(). // SetServiceStatus().
virtual int daemonize(const char* name, DaemonFunc func) = 0; virtual int daemonize(const char* name, DaemonFunc func) = 0;
// directs CLog to send messages to the daemon log. used when
// messages should no longer go to the console. name is used
// in the log to identify this process.
virtual void installDaemonLogger(const char* name) = 0;
// continually restart the given function in a separate process // continually restart the given function in a separate process
// or thread until it exits normally with a code less than the // or thread until it exits normally with a code less than the
// given code then return the code. // given code then return the code.

View File

@ -15,10 +15,8 @@
// platform dependent name of a daemon // platform dependent name of a daemon
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
#define DAEMON "service"
#define DAEMON_NAME "Synergy Server" #define DAEMON_NAME "Synergy Server"
#elif defined(CONFIG_PLATFORM_UNIX) #elif defined(CONFIG_PLATFORM_UNIX)
#define DAEMON "daemon"
#define DAEMON_NAME "synergyd" #define DAEMON_NAME "synergyd"
#endif #endif
@ -36,8 +34,10 @@
static const char* pname = NULL; static const char* pname = NULL;
static bool s_restartable = true; static bool s_restartable = true;
static bool s_daemon = true; static bool s_daemon = true;
#if defined(CONFIG_PLATFORM_WIN32)
static bool s_install = false; static bool s_install = false;
static bool s_uninstall = false; static bool s_uninstall = false;
#endif
static const char* s_configFile = NULL; static const char* s_configFile = NULL;
static const char* s_logFilter = NULL; static const char* s_logFilter = NULL;
static CString s_name; static CString s_name;
@ -211,42 +211,62 @@ static
void void
help() help()
{ {
#if defined(CONFIG_PLATFORM_WIN32)
# define PLATFORM_ARGS \
" {--daemon|--no-daemon}" \
" [--install]\n" \
"or\n" \
" --uninstall\n"
# define PLATFORM_DESC \
" --install install server as a daemon.\n" \
" --uninstall uninstall server daemon.\n"
# define PLATFORM_EXTRA \
"At least one command line argument is required. If you don't otherwise\n" \
"need an argument use `--daemon'.\n" \
"\n"
#else
# define PLATFORM_ARGS \
" [--daemon|--no-daemon]"
# define PLATFORM_DESC
# define PLATFORM_EXTRA
#endif
CPlatform platform; CPlatform platform;
log((CLOG_PRINT log((CLOG_PRINT
"Usage: %s" "Usage: %s"
" [--address <address>]" " [--address <address>]"
" [--config <pathname>]" " [--config <pathname>]"
" [--"DAEMON"|--no-"DAEMON"]"
" [--debug <level>]" " [--debug <level>]"
" [--name <screen-name>]" " [--name <screen-name>]"
" [--restart|--no-restart]\n" " [--restart|--no-restart]\n"
" [--install]\n" PLATFORM_ARGS
"or\n"
" --uninstall\n"
"\n" "\n"
"Start the synergy mouse/keyboard sharing server.\n" "Start the synergy mouse/keyboard sharing server.\n"
"\n" "\n"
" -a, --address <address> listen for clients on the given address.\n" " -a, --address <address> listen for clients on the given address.\n"
" -c, --config <pathname> use the named configuration file instead\n" " -c, --config <pathname> use the named configuration file instead.\n"
" where ~ represents the user's home directory.\n"
" -d, --debug <level> filter out log messages with priorty below level.\n" " -d, --debug <level> filter out log messages with priorty below level.\n"
" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
" DEBUG, DEBUG1, DEBUG2.\n" " DEBUG, DEBUG1, DEBUG2.\n"
" -f, --no-"DAEMON" run the server in the foreground.\n" " -f, --no-daemon run the server in the foreground.\n"
"* --"DAEMON" run the server as a "DAEMON".\n" "* --daemon run the server as a daemon.\n"
" -n, --name <screen-name> use screen-name instead the hostname to identify\n" " -n, --name <screen-name> use screen-name instead the hostname to identify\n"
" this screen in the configuration.\n" " this screen in the configuration.\n"
" -1, --no-restart do not try to restart the server if it fails for\n" " -1, --no-restart do not try to restart the server if it fails for\n"
" some reason.\n" " some reason.\n"
"* --restart restart the server automatically if it fails.\n" "* --restart restart the server automatically if it fails.\n"
" --install install server as a "DAEMON".\n" PLATFORM_DESC
" --uninstall uninstall server "DAEMON".\n"
" -h, --help display this help and exit.\n" " -h, --help display this help and exit.\n"
" --version display version information and exit.\n" " --version display version information and exit.\n"
"\n" "\n"
"* marks defaults.\n" "* marks defaults.\n"
"\n" "\n"
PLATFORM_EXTRA
"The argument for --address is of the form: [<hostname>][:<port>]. The\n" "The argument for --address is of the form: [<hostname>][:<port>]. The\n"
"hostname must be the address or hostname of an interface on the system.\n" "hostname must be the address or hostname of an interface on the system.\n"
"The default is to listen on all interfaces. The port overrides the\n" "The default is to listen on all interfaces. The port overrides the\n"
@ -260,7 +280,7 @@ help()
"defaults with just the server screen.\n" "defaults with just the server screen.\n"
"\n" "\n"
"Where log messages go depends on the platform and whether or not the\n" "Where log messages go depends on the platform and whether or not the\n"
"server is running as a "DAEMON".", "server is running as a daemon.",
pname, pname,
kDefaultPort, kDefaultPort,
platform.addPathComponent( platform.addPathComponent(
@ -355,12 +375,12 @@ parse(
s_configFile = argv[++i]; s_configFile = argv[++i];
} }
else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
// not a daemon // not a daemon
s_daemon = false; s_daemon = false;
} }
else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { else if (isArg(i, argc, argv, NULL, "--daemon")) {
// daemonize // daemonize
s_daemon = true; s_daemon = true;
} }
@ -385,12 +405,8 @@ parse(
bye(0); bye(0);
} }
#if defined(CONFIG_PLATFORM_WIN32)
else if (isArg(i, argc, argv, NULL, "--install")) { else if (isArg(i, argc, argv, NULL, "--install")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_install = true; s_install = true;
if (s_uninstall) { if (s_uninstall) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'" log((CLOG_PRINT "%s: `--install' and `--uninstall'"
@ -399,13 +415,10 @@ parse(
bye(2); bye(2);
} }
} }
else if (isArg(i, argc, argv, NULL, "--uninstall")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif #endif
#if defined(CONFIG_PLATFORM_WIN32)
else if (isArg(i, argc, argv, NULL, "--uninstall")) {
s_uninstall = true; s_uninstall = true;
if (s_install) { if (s_install) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'" log((CLOG_PRINT "%s: `--install' and `--uninstall'"
@ -414,6 +427,7 @@ parse(
bye(2); bye(2);
} }
} }
#endif
else if (isArg(i, argc, argv, "--", NULL)) { else if (isArg(i, argc, argv, "--", NULL)) {
// remaining arguments are not options // remaining arguments are not options
@ -598,16 +612,6 @@ daemonStartup(
return platform->runDaemon(realMain, daemonStop); return platform->runDaemon(realMain, daemonStop);
} }
static
int
daemonStartup95(
IPlatform*,
int,
const char**)
{
return realMain(NULL);
}
static static
bool bool
logDiscard( logDiscard(
@ -664,12 +668,13 @@ WinMain(
// ignore // ignore
} }
// send PRINT and FATAL output to a message box
CLog::setOutputter(&logMessageBox);
// if we're not starting as an NT service then reparse the command // if we're not starting as an NT service then reparse the command
// line normally. // line normally.
if (s_die || !s_daemon || s_install || s_uninstall || if (s_die || __argc > 1 || s_install || s_uninstall ||
CWin32Platform::isWindows95Family()) { CWin32Platform::isWindows95Family()) {
// send PRINT and FATAL output to a message box
CLog::setOutputter(&logMessageBox);
// exit on bye // exit on bye
bye = &exit; bye = &exit;
@ -678,9 +683,9 @@ WinMain(
parse(__argc, const_cast<const char**>(__argv)); parse(__argc, const_cast<const char**>(__argv));
} }
// if starting as a daemon then we ignore the startup command line // if no arguments were provided then we're hopefully starting as
// here. we'll parse the command line passed in when the service // a service. we'll parse the command line passed in when the
// control manager calls us back. // service control manager calls us back.
else { else {
// do nothing // do nothing
} }
@ -697,7 +702,7 @@ WinMain(
// construct the command line to start the service with // construct the command line to start the service with
CString commandLine; CString commandLine;
commandLine += "--"DAEMON; commandLine += "--daemon";
if (s_restartable) { if (s_restartable) {
commandLine += " --restart"; commandLine += " --restart";
} }
@ -725,12 +730,19 @@ WinMain(
return 0; return 0;
} }
else if (s_uninstall) { else if (s_uninstall) {
if (!platform.uninstallDaemon(DAEMON_NAME)) { switch (platform.uninstallDaemon(DAEMON_NAME)) {
case IPlatform::kSuccess:
log((CLOG_PRINT "uninstalled successfully"));
return 0;
case IPlatform::kFailed:
log((CLOG_CRIT "failed to uninstall service")); log((CLOG_CRIT "failed to uninstall service"));
return 16; return 16;
case IPlatform::kAlready:
log((CLOG_CRIT "service isn't installed"));
return 16;
} }
log((CLOG_PRINT "uninstalled successfully"));
return 0;
} }
// load configuration // load configuration
@ -740,17 +752,23 @@ WinMain(
int result; int result;
if (s_daemon) { if (s_daemon) {
if (CWin32Platform::isWindows95Family()) { if (CWin32Platform::isWindows95Family()) {
result = platform.daemonize(DAEMON_NAME, &daemonStartup95); result = -1;
} }
else { else {
result = platform.daemonize(DAEMON_NAME, &daemonStartup); result = platform.daemonize(DAEMON_NAME, &daemonStartup);
} }
if (result == -1) { if (result == -1) {
log((CLOG_CRIT "failed to start as a service")); log((CLOG_CRIT "failed to start as a service" BYE, pname));
return 16; return 16;
} }
} }
else { else {
// if a daemon then redirect log
if (s_daemon) {
platform.installDaemonLogger(__argv[0]);
}
// run
result = restartableMain(); result = restartableMain();
} }