diff --git a/base/CFunctionJob.cpp b/base/CFunctionJob.cpp index 7558c911..423f0a63 100644 --- a/base/CFunctionJob.cpp +++ b/base/CFunctionJob.cpp @@ -11,6 +11,11 @@ CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) : // do nothing } +CFunctionJob::~CFunctionJob() +{ + // do nothing +} + void CFunctionJob::run() { diff --git a/base/CFunctionJob.h b/base/CFunctionJob.h index f3b5272d..b1c786e0 100644 --- a/base/CFunctionJob.h +++ b/base/CFunctionJob.h @@ -6,6 +6,7 @@ class CFunctionJob : public IJob { public: CFunctionJob(void (*func)(void*), void* arg = NULL); + virtual ~CFunctionJob(); // IJob overrides virtual void run(); diff --git a/base/CLog.cpp b/base/CLog.cpp index a35ca588..bb9679d6 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -65,6 +65,11 @@ CLog::print( fmt += 3; } + // done if below priority threshold + if (priority > getMaxPriority()) { + return; + } + // compute prefix padding length int pad = g_priorityPad; @@ -98,6 +103,11 @@ CLog::printt( fmt += 3; } + // done if below priority threshold + if (priority > getMaxPriority()) { + return; + } + // compute prefix padding length char stack[1024]; sprintf(stack, "%d", line); @@ -217,34 +227,32 @@ CLog::output( char* msg) { assert(priority >= -1 && priority < g_numPriority); - assert(msg != 0); + assert(msg != NULL); - if (priority <= getMaxPriority()) { - // insert priority label - int n = -g_prioritySuffixLength; - if (priority >= 0) { - n = strlen(g_priority[priority]); - sprintf(msg + g_maxPriorityLength - n, - "%s:", g_priority[priority]); - msg[g_maxPriorityLength + 1] = ' '; - } + // insert priority label + int n = -g_prioritySuffixLength; + if (priority >= 0) { + n = strlen(g_priority[priority]); + sprintf(msg + g_maxPriorityLength - n, + "%s:", g_priority[priority]); + msg[g_maxPriorityLength + 1] = ' '; + } - // put a newline at the end + // put a newline at the end #if defined(CONFIG_PLATFORM_WIN32) - strcat(msg + g_priorityPad, "\r\n"); + strcat(msg + g_priorityPad, "\r\n"); #else - strcat(msg + g_priorityPad, "\n"); + strcat(msg + g_priorityPad, "\n"); #endif - // print it - CHoldLock lock(s_lock); - if (s_outputter == NULL || - !s_outputter(priority, msg + g_maxPriorityLength - n)) { + // print it + CHoldLock lock(s_lock); + if (s_outputter == NULL || + !s_outputter(priority, msg + g_maxPriorityLength - n)) { #if defined(CONFIG_PLATFORM_WIN32) - openConsole(); + openConsole(); #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 - buffer = 0; + buffer = NULL; do { delete[] buffer; len *= 2; diff --git a/base/TMethodJob.h b/base/TMethodJob.h index 59a22208..950b95c8 100644 --- a/base/TMethodJob.h +++ b/base/TMethodJob.h @@ -7,6 +7,7 @@ template class TMethodJob : public IJob { public: TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); + virtual ~TMethodJob(); // IJob overrides virtual void run(); @@ -27,6 +28,13 @@ TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : // do nothing } +template +inline +TMethodJob::~TMethodJob() +{ + // do nothing +} + template inline void diff --git a/base/common.h b/base/common.h index e31a1b73..df7d0a20 100644 --- a/base/common.h +++ b/base/common.h @@ -30,6 +30,12 @@ // this one's a little too aggressive #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) #else diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 895ae78f..b0fbdcf1 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -16,6 +16,7 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : m_client(NULL), m_threadID(0), + m_lastThreadID(0), m_desk(NULL), m_deskName(), m_window(NULL), @@ -741,17 +742,23 @@ CMSWindowsSecondaryScreen::syncDesktop() const { // note -- mutex must be locked on entry - DWORD threadID = GetCurrentThreadId(); + // change calling thread's desktop if (!m_is95Family) { - if (GetThreadDesktop(threadID) != m_desk) { - // FIXME -- this doesn't work. if we set a desktop then - // sending events doesn't work. - if (SetThreadDesktop(m_desk) == 0) { - log((CLOG_ERR "failed to set desktop: %d", GetLastError())); - } + if (SetThreadDesktop(m_desk) == 0) { + log((CLOG_WARN "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 diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 176203f2..084ed47c 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -89,6 +89,9 @@ private: // the main loop's thread id DWORD m_threadID; + // the thread id of the last attached thread + mutable DWORD m_lastThreadID; + // the current desk and it's name HDESK m_desk; CString m_deskName; diff --git a/client/client.cpp b/client/client.cpp index b7c7214c..fbd89dfa 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -16,10 +16,8 @@ // platform dependent name of a daemon #if defined(CONFIG_PLATFORM_WIN32) -#define DAEMON "service" #define DAEMON_NAME "Synergy Client" #elif defined(CONFIG_PLATFORM_UNIX) -#define DAEMON "daemon" #define DAEMON_NAME "synergy" #endif @@ -127,8 +125,6 @@ realMain( // terminated return 1; } - - return 0; } static @@ -182,17 +178,34 @@ static void help() { +#if defined(CONFIG_PLATFORM_WIN32) + +# define PLATFORM_ARGS \ +" [--install]" \ +" \n" \ +"or\n" \ +" --uninstall\n" +# define PLATFORM_DESC \ +" --install install server as a service.\n" \ +" --uninstall uninstall server service.\n" + +#else + +# define PLATFORM_ARGS \ +" \n" +# define PLATFORM_DESC + +#endif + log((CLOG_PRINT "Usage: %s" -" [--"DAEMON"|--no-"DAEMON"]" " [--camp|--no-camp]" +" [--daemon|--no-daemon]" " [--debug ]" " [--name ]" " [--restart|--no-restart]" -" [--install]" -" \n" -"or\n" -" --uninstall\n" +PLATFORM_ARGS +"\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" "* --camp keep attempting to connect to the server until\n" @@ -201,8 +214,8 @@ help() " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" -" -f, --no-"DAEMON" run the client in the foreground.\n" -"* --"DAEMON" run the client as a "DAEMON".\n" +" -f, --no-daemon run the client in the foreground.\n" +"* --daemon run the client as a daemon.\n" " -n, --name use screen-name instead the hostname to identify\n" " ourself to the server.\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" " some unexpected reason, including the server\n" " disconnecting but not including failing to\n" -" connect to the server." -" --install install server as a "DAEMON".\n" -" --uninstall uninstall server "DAEMON".\n" +" connect to the server.\n" +PLATFORM_DESC " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" @@ -223,7 +235,7 @@ help() "default port, %d.\n" "\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)); } @@ -291,12 +303,12 @@ parse( s_camp = false; } - else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon s_daemon = false; } - else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { + else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize s_daemon = true; } @@ -321,12 +333,8 @@ parse( bye(0); } +#if defined(CONFIG_PLATFORM_WIN32) 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; if (s_uninstall) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -335,13 +343,10 @@ parse( 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 + +#if defined(CONFIG_PLATFORM_WIN32) + else if (isArg(i, argc, argv, NULL, "--uninstall")) { s_uninstall = true; if (s_install) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -350,6 +355,7 @@ parse( bye(2); } } +#endif else if (isArg(i, argc, argv, "--", NULL)) { // remaining arguments are not options @@ -490,16 +496,6 @@ daemonStartup( return platform->runDaemon(realMain, daemonStop); } -static -int -daemonStartup95( - IPlatform*, - int, - const char**) -{ - return realMain(NULL); -} - static bool logDiscard( @@ -556,12 +552,13 @@ WinMain( // 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 // line normally. - if (s_die || !s_daemon || s_install || s_uninstall || + if (s_die || __argc > 1 || s_install || s_uninstall || CWin32Platform::isWindows95Family()) { - // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); // exit on bye bye = &exit; @@ -570,9 +567,9 @@ WinMain( parse(__argc, const_cast(__argv)); } - // if starting as a daemon then we ignore the startup command line - // here. we'll parse the command line passed in when the service - // control manager calls us back. + // if no arguments were provided then we're hopefully starting as + // a service. we'll parse the command line passed in when the + // service control manager calls us back. else { // do nothing } @@ -588,7 +585,7 @@ WinMain( } // construct the command line to start the service with - CString commandLine = "--"DAEMON; + CString commandLine = "--daemon"; if (s_restartable) { commandLine += " --restart"; } @@ -613,29 +610,42 @@ WinMain( return 0; } 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")); return 16; + + case IPlatform::kAlready: + log((CLOG_CRIT "service isn't installed")); + return 16; } - log((CLOG_PRINT "uninstalled successfully")); - return 0; } // daemonize if requested int result; - if (s_daemon) { + if (__argc <= 1) { if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + result = -1; } else { result = platform.daemonize(DAEMON_NAME, &daemonStartup); } 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; } } else { + // if a daemon then redirect log + if (s_daemon) { + platform.installDaemonLogger(__argv[0]); + } + + // run result = restartableMain(); } diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index fee8546b..ca6b63e9 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -7,7 +7,8 @@ const UInt32 CStreamBuffer::kChunkSize = 4096; CStreamBuffer::CStreamBuffer() : - m_size(0) + m_size(0), + m_headUsed(0) { // do nothing } @@ -25,17 +26,17 @@ CStreamBuffer::peek( // reserve space in first chunk 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 ChunkList::iterator scan = head; ++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()); scan = m_chunks.erase(scan); } - return reinterpret_cast(head->begin()); + return reinterpret_cast(head->begin() + m_headUsed); } void @@ -44,7 +45,8 @@ CStreamBuffer::pop( { // discard all chunks if n is greater than or equal to m_size if (n >= m_size) { - m_size = 0; + m_size = 0; + m_headUsed = 0; m_chunks.clear(); return; } @@ -55,15 +57,16 @@ CStreamBuffer::pop( // discard chunks until more than n bytes would've been discarded ChunkList::iterator scan = m_chunks.begin(); assert(scan != m_chunks.end()); - while (scan->size() <= n) { - n -= scan->size(); - scan = m_chunks.erase(scan); + while (scan->size() - m_headUsed <= n) { + n -= scan->size() - m_headUsed; + m_headUsed = 0; + scan = m_chunks.erase(scan); assert(scan != m_chunks.end()); } // remove left over bytes from the head chunk if (n > 0) { - scan->erase(scan->begin(), scan->begin() + n); + m_headUsed += n; } } diff --git a/io/CStreamBuffer.h b/io/CStreamBuffer.h index 97b32296..a975e77d 100644 --- a/io/CStreamBuffer.h +++ b/io/CStreamBuffer.h @@ -33,6 +33,7 @@ private: ChunkList m_chunks; UInt32 m_size; + UInt32 m_headUsed; }; #endif diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 924509f4..74d3d727 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -105,6 +105,16 @@ CThread::wait( 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 CThread::testCancel() { diff --git a/mt/CThread.h b/mt/CThread.h index 03dc3fd3..81c8aef8 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -1,6 +1,8 @@ #ifndef CTHREAD_H #define CTHREAD_H +#include "common.h" + class IJob; class CThreadRep; @@ -103,6 +105,13 @@ public: // (cancellation point) 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 // immediately if called by a thread on itself. returns NULL for // threads that were cancelled. diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 06af394a..3ef5ec18 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -614,7 +614,45 @@ CThreadRep::wait( testCancel(); 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; } } diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index b407ae88..448d2b4d 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -43,6 +43,11 @@ public: // wait for thread to exit or for current thread to cancel 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 void setPriority(int n); diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index f093c770..693cfd84 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -202,9 +202,7 @@ CMSWindowsScreen::getEvent( MSG* msg) const { // wait for an event in a cancellable way - while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) { - CThread::sleep(0.01); - } + CThread::waitForEvent(); GetMessage(msg, NULL, 0, 0); } diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index aba109e7..1ab2c63b 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -35,12 +35,12 @@ CUnixPlatform::installDaemon( return true; } -bool +CUnixPlatform::EResult CUnixPlatform::uninstallDaemon( const char*) { // daemons don't require special installation - return true; + return kSuccess; } int @@ -85,12 +85,20 @@ CUnixPlatform::daemonize( dup(1); // hook up logger - setDaemonLogger(name); + installDaemonLogger(name); // invoke function return func(this, 1, &name); } +void +CUnixPlatform::installDaemonLogger( + const char* name) +{ + openlog(name, 0, LOG_DAEMON); + CLog::setOutputter(&CUnixPlatform::deamonLogger); +} + int CUnixPlatform::restart( RestartFunc func, @@ -195,14 +203,6 @@ CUnixPlatform::addPathComponent( return path; } -void -CUnixPlatform::setDaemonLogger( - const char* name) -{ - openlog(name, 0, LOG_DAEMON); - CLog::setOutputter(&CUnixPlatform::deamonLogger); -} - bool CUnixPlatform::deamonLogger( int priority, diff --git a/platform/CUnixPlatform.h b/platform/CUnixPlatform.h index d5a92ec3..9b5592b4 100644 --- a/platform/CUnixPlatform.h +++ b/platform/CUnixPlatform.h @@ -13,8 +13,9 @@ public: const char* description, const char* pathname, const char* commandLine); - virtual bool uninstallDaemon(const char* name); + virtual EResult uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); + virtual void installDaemonLogger(const char* name); virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; @@ -23,9 +24,6 @@ public: const CString& prefix, const CString& suffix) const; -protected: - virtual void setDaemonLogger(const char* name); - private: static bool deamonLogger(int, const char*); }; diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp index 75dc0c37..58cd7cd2 100644 --- a/platform/CWin32Platform.cpp +++ b/platform/CWin32Platform.cpp @@ -180,7 +180,7 @@ CWin32Platform::installDaemon( } } -bool +IPlatform::EResult CWin32Platform::uninstallDaemon( const char* name) { @@ -190,7 +190,7 @@ CWin32Platform::uninstallDaemon( HKEY key = open95ServicesKey(); if (key == NULL) { log((CLOG_ERR "cannot open RunServices registry key", GetLastError())); - return false; + return kAlready; } // remove entry @@ -199,7 +199,7 @@ CWin32Platform::uninstallDaemon( // clean up closeKey(key); - return true; + return kSuccess; } // windows NT family services @@ -216,26 +216,41 @@ CWin32Platform::uninstallDaemon( SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { log((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); - return false; + return kFailed; } // open the service. oddly, you must open a service to delete it. - bool success; + EResult result; SC_HANDLE service = OpenService(mgr, name, DELETE); if (service == NULL) { - log((CLOG_ERR "OpenService failed with %d", GetLastError())); - success = false; + const DWORD e = GetLastError(); + log((CLOG_ERR "OpenService failed with %d", e)); + result = (e == ERROR_SERVICE_DOES_NOT_EXIST) ? kAlready : kFailed; } 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); } // close the manager 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 CWin32Platform::restart( RestartFunc func, @@ -643,12 +673,7 @@ CWin32Platform::serviceMain( const char** argv = const_cast(argvIn); // open event log and direct log messages to it - if (s_eventLog == NULL) { - s_eventLog = RegisterEventSource(NULL, argv[0]); - if (s_eventLog != NULL) { - CLog::setOutputter(&CWin32Platform::serviceLogger); - } - } + installDaemonLogger(argv[0]); // create synchronization objects CThread::init(); diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 40dfbe29..45f99e29 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -46,8 +46,9 @@ public: const char* description, const char* pathname, const char* commandLine); - virtual bool uninstallDaemon(const char* name); + virtual EResult uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); + virtual void installDaemonLogger(const char* name); virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; diff --git a/platform/IPlatform.h b/platform/IPlatform.h index a5d0bde9..0a7e4cee 100644 --- a/platform/IPlatform.h +++ b/platform/IPlatform.h @@ -9,6 +9,12 @@ public: typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); typedef int (*RestartFunc)(); + enum EResult { + kSuccess, + kFailed, + kAlready + }; + // manipulators // install/uninstall a daemon. commandLine should *not* @@ -18,12 +24,10 @@ public: const char* description, const char* pathname, 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 - // messages to a system message logger since messages can no - // longer go to the console. returns true iff successful. - // the name is the name of the daemon. + // daemonize. this should call installDaemonLogger(). returns + // true iff successful. the name is the name of the daemon. // daemonize. this should have the side effect of sending log // messages to a system message logger since messages can no @@ -44,6 +48,11 @@ public: // SetServiceStatus(). 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 // or thread until it exits normally with a code less than the // given code then return the code. diff --git a/server/server.cpp b/server/server.cpp index aca20d12..0072cb8f 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -15,10 +15,8 @@ // platform dependent name of a daemon #if defined(CONFIG_PLATFORM_WIN32) -#define DAEMON "service" #define DAEMON_NAME "Synergy Server" #elif defined(CONFIG_PLATFORM_UNIX) -#define DAEMON "daemon" #define DAEMON_NAME "synergyd" #endif @@ -36,8 +34,10 @@ static const char* pname = NULL; static bool s_restartable = true; static bool s_daemon = true; +#if defined(CONFIG_PLATFORM_WIN32) static bool s_install = false; static bool s_uninstall = false; +#endif static const char* s_configFile = NULL; static const char* s_logFilter = NULL; static CString s_name; @@ -211,42 +211,62 @@ static void 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; log((CLOG_PRINT "Usage: %s" " [--address
]" " [--config ]" -" [--"DAEMON"|--no-"DAEMON"]" " [--debug ]" " [--name ]" " [--restart|--no-restart]\n" -" [--install]\n" -"or\n" -" --uninstall\n" +PLATFORM_ARGS "\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" " -a, --address
listen for clients on the given address.\n" -" -c, --config use the named configuration file instead\n" -" where ~ represents the user's home directory.\n" +" -c, --config use the named configuration file instead.\n" " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" -" -f, --no-"DAEMON" run the server in the foreground.\n" -"* --"DAEMON" run the server as a "DAEMON".\n" +" -f, --no-daemon run the server in the foreground.\n" +"* --daemon run the server as a daemon.\n" " -n, --name use screen-name instead the hostname to identify\n" " this screen in the configuration.\n" " -1, --no-restart do not try to restart the server if it fails for\n" " some reason.\n" "* --restart restart the server automatically if it fails.\n" -" --install install server as a "DAEMON".\n" -" --uninstall uninstall server "DAEMON".\n" +PLATFORM_DESC " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" "* marks defaults.\n" "\n" +PLATFORM_EXTRA "The argument for --address is of the form: [][:]. The\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" @@ -260,7 +280,7 @@ help() "defaults with just the server screen.\n" "\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, kDefaultPort, platform.addPathComponent( @@ -355,12 +375,12 @@ parse( 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 s_daemon = false; } - else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { + else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize s_daemon = true; } @@ -385,12 +405,8 @@ parse( bye(0); } +#if defined(CONFIG_PLATFORM_WIN32) 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; if (s_uninstall) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -399,13 +415,10 @@ parse( 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 + +#if defined(CONFIG_PLATFORM_WIN32) + else if (isArg(i, argc, argv, NULL, "--uninstall")) { s_uninstall = true; if (s_install) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -414,6 +427,7 @@ parse( bye(2); } } +#endif else if (isArg(i, argc, argv, "--", NULL)) { // remaining arguments are not options @@ -598,16 +612,6 @@ daemonStartup( return platform->runDaemon(realMain, daemonStop); } -static -int -daemonStartup95( - IPlatform*, - int, - const char**) -{ - return realMain(NULL); -} - static bool logDiscard( @@ -664,12 +668,13 @@ WinMain( // 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 // line normally. - if (s_die || !s_daemon || s_install || s_uninstall || + if (s_die || __argc > 1 || s_install || s_uninstall || CWin32Platform::isWindows95Family()) { - // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); // exit on bye bye = &exit; @@ -678,9 +683,9 @@ WinMain( parse(__argc, const_cast(__argv)); } - // if starting as a daemon then we ignore the startup command line - // here. we'll parse the command line passed in when the service - // control manager calls us back. + // if no arguments were provided then we're hopefully starting as + // a service. we'll parse the command line passed in when the + // service control manager calls us back. else { // do nothing } @@ -697,7 +702,7 @@ WinMain( // construct the command line to start the service with CString commandLine; - commandLine += "--"DAEMON; + commandLine += "--daemon"; if (s_restartable) { commandLine += " --restart"; } @@ -725,12 +730,19 @@ WinMain( return 0; } 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")); return 16; + + case IPlatform::kAlready: + log((CLOG_CRIT "service isn't installed")); + return 16; } - log((CLOG_PRINT "uninstalled successfully")); - return 0; } // load configuration @@ -740,17 +752,23 @@ WinMain( int result; if (s_daemon) { if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + result = -1; } else { result = platform.daemonize(DAEMON_NAME, &daemonStartup); } 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; } } else { + // if a daemon then redirect log + if (s_daemon) { + platform.installDaemonLogger(__argv[0]); + } + + // run result = restartableMain(); }