Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

synergys.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2002 Chris Schoeneman
00004  * 
00005  * This package is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * found in the file COPYING that should have accompanied this file.
00008  * 
00009  * This package is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "CClientListener.h"
00016 #include "CClientProxy.h"
00017 #include "CConfig.h"
00018 #include "CPrimaryClient.h"
00019 #include "CServer.h"
00020 #include "CScreen.h"
00021 #include "ProtocolTypes.h"
00022 #include "Version.h"
00023 #include "XScreen.h"
00024 #include "CSocketMultiplexer.h"
00025 #include "CTCPSocketFactory.h"
00026 #include "XSocket.h"
00027 #include "CThread.h"
00028 #include "CEventQueue.h"
00029 #include "CFunctionEventJob.h"
00030 #include "CLog.h"
00031 #include "CString.h"
00032 #include "CStringUtil.h"
00033 #include "LogOutputters.h"
00034 #include "CArch.h"
00035 #include "XArch.h"
00036 #include "stdfstream.h"
00037 #include <cstring>
00038 
00039 #define DAEMON_RUNNING(running_)
00040 #if WINAPI_MSWINDOWS
00041 #include "CArchMiscWindows.h"
00042 #include "CMSWindowsScreen.h"
00043 #include "CMSWindowsUtil.h"
00044 #include "CMSWindowsServerTaskBarReceiver.h"
00045 #include "resource.h"
00046 #undef DAEMON_RUNNING
00047 #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
00048 #elif WINAPI_XWINDOWS
00049 #include "CXWindowsScreen.h"
00050 #include "CXWindowsServerTaskBarReceiver.h"
00051 #elif WINAPI_CARBON
00052 #include "COSXScreen.h"
00053 #include "COSXServerTaskBarReceiver.h"
00054 #endif
00055 
00056 // platform dependent name of a daemon
00057 #if SYSAPI_WIN32
00058 #define DAEMON_NAME "Synergy Server"
00059 #elif SYSAPI_UNIX
00060 #define DAEMON_NAME "synergys"
00061 #endif
00062 
00063 // configuration file name
00064 #if SYSAPI_WIN32
00065 #define USR_CONFIG_NAME "synergy.sgc"
00066 #define SYS_CONFIG_NAME "synergy.sgc"
00067 #elif SYSAPI_UNIX
00068 #define USR_CONFIG_NAME ".synergy.conf"
00069 #define SYS_CONFIG_NAME "synergy.conf"
00070 #endif
00071 
00072 typedef int (*StartupFunc)(int, char**);
00073 static void parse(int argc, const char* const* argv);
00074 static bool loadConfig(const CString& pathname);
00075 static void loadConfig();
00076 
00077 //
00078 // program arguments
00079 //
00080 
00081 #define ARG CArgs::s_instance
00082 
00083 class CArgs {
00084 public:
00085     CArgs() :
00086         m_pname(NULL),
00087         m_backend(false),
00088         m_restartable(true),
00089         m_daemon(true),
00090         m_configFile(),
00091         m_logFilter(NULL),
00092         m_display(NULL),
00093         m_synergyAddress(NULL),
00094         m_config(NULL)
00095         { s_instance = this; }
00096     ~CArgs() { s_instance = NULL; }
00097 
00098 public:
00099     static CArgs*       s_instance;
00100     const char*         m_pname;
00101     bool                m_backend;
00102     bool                m_restartable;
00103     bool                m_daemon;
00104     CString             m_configFile;
00105     const char*         m_logFilter;
00106     const char*         m_display;
00107     CString             m_name;
00108     CNetworkAddress*    m_synergyAddress;
00109     CConfig*            m_config;
00110 };
00111 
00112 CArgs*                  CArgs::s_instance = NULL;
00113 
00114 
00115 //
00116 // platform dependent factories
00117 //
00118 
00119 static
00120 CScreen*
00121 createScreen()
00122 {
00123 #if WINAPI_MSWINDOWS
00124     return new CScreen(new CMSWindowsScreen(true));
00125 #elif WINAPI_XWINDOWS
00126     return new CScreen(new CXWindowsScreen(ARG->m_display, true));
00127 #elif WINAPI_CARBON
00128     return new CScreen(new COSXScreen(true));
00129 #endif
00130 }
00131 
00132 static
00133 CServerTaskBarReceiver*
00134 createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
00135 {
00136 #if WINAPI_MSWINDOWS
00137     return new CMSWindowsServerTaskBarReceiver(
00138                             CMSWindowsScreen::getInstance(), logBuffer);
00139 #elif WINAPI_XWINDOWS
00140     return new CXWindowsServerTaskBarReceiver(logBuffer);
00141 #elif WINAPI_CARBON
00142     return new COSXServerTaskBarReceiver(logBuffer);
00143 #endif
00144 }
00145 
00146 
00147 //
00148 // platform independent main
00149 //
00150 
00151 enum EServerState {
00152     kUninitialized,
00153     kInitializing,
00154     kInitializingToStart,
00155     kInitialized,
00156     kStarting,
00157     kStarted
00158 };
00159 
00160 static EServerState             s_serverState         = kUninitialized;
00161 static CServer*                 s_server              = NULL;
00162 static CScreen*                 s_serverScreen        = NULL;
00163 static CPrimaryClient*          s_primaryClient       = NULL;
00164 static CClientListener*         s_listener            = NULL;
00165 static CServerTaskBarReceiver*  s_taskBarReceiver     = NULL;
00166 static CEvent::Type             s_reloadConfigEvent   = CEvent::kUnknown;
00167 static CEvent::Type             s_forceReconnectEvent = CEvent::kUnknown;
00168 static bool                     s_suspended           = false;
00169 static CEventQueueTimer*        s_timer               = NULL;
00170 
00171 CEvent::Type
00172 getReloadConfigEvent()
00173 {
00174     return CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig");
00175 }
00176 
00177 CEvent::Type
00178 getForceReconnectEvent()
00179 {
00180     return CEvent::registerTypeOnce(s_forceReconnectEvent, "forceReconnect");
00181 }
00182 
00183 static
00184 void
00185 updateStatus()
00186 {
00187     s_taskBarReceiver->updateStatus(s_server, "");
00188 }
00189 
00190 static
00191 void
00192 updateStatus(const CString& msg)
00193 {
00194     s_taskBarReceiver->updateStatus(s_server, msg);
00195 }
00196 
00197 static
00198 void
00199 handleClientConnected(const CEvent&, void* vlistener)
00200 {
00201     CClientListener* listener = reinterpret_cast<CClientListener*>(vlistener);
00202     CClientProxy* client = listener->getNextClient();
00203     if (client != NULL) {
00204         s_server->adoptClient(client);
00205         updateStatus();
00206     }
00207 }
00208 
00209 static
00210 CClientListener*
00211 openClientListener(const CNetworkAddress& address)
00212 {
00213     CClientListener* listen =
00214         new CClientListener(address, new CTCPSocketFactory, NULL);
00215     EVENTQUEUE->adoptHandler(CClientListener::getConnectedEvent(), listen,
00216                             new CFunctionEventJob(
00217                                 &handleClientConnected, listen));
00218     return listen;
00219 }
00220 
00221 static
00222 void
00223 closeClientListener(CClientListener* listen)
00224 {
00225     if (listen != NULL) {
00226         EVENTQUEUE->removeHandler(CClientListener::getConnectedEvent(), listen);
00227         delete listen;
00228     }
00229 }
00230 
00231 static
00232 void
00233 handleScreenError(const CEvent&, void*)
00234 {
00235     LOG((CLOG_CRIT "error on screen"));
00236     EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00237 }
00238 
00239 
00240 static void handleSuspend(const CEvent& event, void*);
00241 static void handleResume(const CEvent& event, void*);
00242 
00243 static
00244 CScreen*
00245 openServerScreen()
00246 {
00247     CScreen* screen = createScreen();
00248     EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
00249                             screen->getEventTarget(),
00250                             new CFunctionEventJob(
00251                                 &handleScreenError));
00252     EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(),
00253                             screen->getEventTarget(),
00254                             new CFunctionEventJob(
00255                                 &handleSuspend));
00256     EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(),
00257                             screen->getEventTarget(),
00258                             new CFunctionEventJob(
00259                                 &handleResume));
00260     return screen;
00261 }
00262 
00263 static
00264 void
00265 closeServerScreen(CScreen* screen)
00266 {
00267     if (screen != NULL) {
00268         EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
00269                             screen->getEventTarget());
00270         EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(),
00271                             screen->getEventTarget());
00272         EVENTQUEUE->removeHandler(IScreen::getResumeEvent(),
00273                             screen->getEventTarget());
00274         delete screen;
00275     }
00276 }
00277 
00278 static
00279 CPrimaryClient*
00280 openPrimaryClient(const CString& name, CScreen* screen)
00281 {
00282     LOG((CLOG_DEBUG1 "creating primary screen"));
00283     return new CPrimaryClient(name, screen);
00284 }
00285 
00286 static
00287 void
00288 closePrimaryClient(CPrimaryClient* primaryClient)
00289 {
00290     delete primaryClient;
00291 }
00292 
00293 static
00294 void
00295 handleNoClients(const CEvent&, void*)
00296 {
00297     updateStatus();
00298 }
00299 
00300 static
00301 void
00302 handleClientsDisconnected(const CEvent&, void*)
00303 {
00304     EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00305 }
00306 
00307 static
00308 CServer*
00309 openServer(const CConfig& config, CPrimaryClient* primaryClient)
00310 {
00311     CServer* server = new CServer(config, primaryClient);
00312     EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server,
00313                         new CFunctionEventJob(handleNoClients));
00314     return server;
00315 }
00316 
00317 static
00318 void
00319 closeServer(CServer* server)
00320 {
00321     if (server == NULL) {
00322         return;
00323     }
00324 
00325     // tell all clients to disconnect
00326     server->disconnect();
00327 
00328     // wait for clients to disconnect for up to timeout seconds
00329     double timeout = 3.0;
00330     CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL);
00331     EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
00332                         new CFunctionEventJob(handleClientsDisconnected));
00333     EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server,
00334                         new CFunctionEventJob(handleClientsDisconnected));
00335     CEvent event;
00336     EVENTQUEUE->getEvent(event);
00337     while (event.getType() != CEvent::kQuit) {
00338         EVENTQUEUE->dispatchEvent(event);
00339         CEvent::deleteData(event);
00340         EVENTQUEUE->getEvent(event);
00341     }
00342     EVENTQUEUE->removeHandler(CEvent::kTimer, timer);
00343     EVENTQUEUE->deleteTimer(timer);
00344     EVENTQUEUE->removeHandler(CServer::getDisconnectedEvent(), server);
00345 
00346     // done with server
00347     delete server;
00348 }
00349 
00350 static bool initServer();
00351 static bool startServer();
00352 
00353 static
00354 void
00355 stopRetryTimer()
00356 {
00357     if (s_timer != NULL) {
00358         EVENTQUEUE->deleteTimer(s_timer);
00359         EVENTQUEUE->removeHandler(CEvent::kTimer, NULL);
00360         s_timer = NULL;
00361     }
00362 }
00363 
00364 static
00365 void
00366 retryHandler(const CEvent&, void*)
00367 {
00368     // discard old timer
00369     assert(s_timer != NULL);
00370     stopRetryTimer();
00371 
00372     // try initializing/starting the server again
00373     switch (s_serverState) {
00374     case kUninitialized:
00375     case kInitialized:
00376     case kStarted:
00377         assert(0 && "bad internal server state");
00378         break;
00379 
00380     case kInitializing:
00381         LOG((CLOG_DEBUG1 "retry server initialization"));
00382         s_serverState = kUninitialized;
00383         if (!initServer()) {
00384             EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00385         }
00386         break;
00387 
00388     case kInitializingToStart:
00389         LOG((CLOG_DEBUG1 "retry server initialization"));
00390         s_serverState = kUninitialized;
00391         if (!initServer()) {
00392             EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00393         }
00394         else if (s_serverState == kInitialized) {
00395             LOG((CLOG_DEBUG1 "starting server"));
00396             if (!startServer()) {
00397                 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00398             }
00399         }
00400         break;
00401 
00402     case kStarting:
00403         LOG((CLOG_DEBUG1 "retry starting server"));
00404         s_serverState = kInitialized;
00405         if (!startServer()) {
00406             EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00407         }
00408         break;
00409     }
00410 }
00411 
00412 static
00413 bool
00414 initServer()
00415 {
00416     // skip if already initialized or initializing
00417     if (s_serverState != kUninitialized) {
00418         return true;
00419     }
00420 
00421     double retryTime;
00422     CScreen* serverScreen         = NULL;
00423     CPrimaryClient* primaryClient = NULL;
00424     try {
00425         CString name    = ARG->m_config->getCanonicalName(ARG->m_name);
00426         serverScreen    = openServerScreen();
00427         primaryClient   = openPrimaryClient(name, serverScreen);
00428         s_serverScreen  = serverScreen;
00429         s_primaryClient = primaryClient;
00430         s_serverState   = kInitialized;
00431         updateStatus();
00432         return true;
00433     }
00434     catch (XScreenUnavailable& e) {
00435         LOG((CLOG_WARN "cannot open primary screen: %s", e.what()));
00436         closePrimaryClient(primaryClient);
00437         closeServerScreen(serverScreen);
00438         updateStatus(CString("cannot open primary screen: ") + e.what());
00439         retryTime = e.getRetryTime();
00440     }
00441     catch (XScreenOpenFailure& e) {
00442         LOG((CLOG_CRIT "cannot open primary screen: %s", e.what()));
00443         closePrimaryClient(primaryClient);
00444         closeServerScreen(serverScreen);
00445         return false;
00446     }
00447     catch (XBase& e) {
00448         LOG((CLOG_CRIT "failed to start server: %s", e.what()));
00449         closePrimaryClient(primaryClient);
00450         closeServerScreen(serverScreen);
00451         return false;
00452     }
00453     
00454     if (ARG->m_restartable) {
00455         // install a timer and handler to retry later
00456         assert(s_timer == NULL);
00457         LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
00458         s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
00459         EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer,
00460                             new CFunctionEventJob(&retryHandler, NULL));
00461         s_serverState = kInitializing;
00462         return true;
00463     }
00464     else {
00465         // don't try again
00466         return false;
00467     }
00468 }
00469 
00470 static
00471 bool
00472 startServer()
00473 {
00474     // skip if already started or starting
00475     if (s_serverState == kStarting || s_serverState == kStarted) {
00476         return true;
00477     }
00478 
00479     // initialize if necessary
00480     if (s_serverState != kInitialized) {
00481         if (!initServer()) {
00482             // hard initialization failure
00483             return false;
00484         }
00485         if (s_serverState == kInitializing) {
00486             // not ready to start
00487             s_serverState = kInitializingToStart;
00488             return true;
00489         }
00490         assert(s_serverState == kInitialized);
00491     }
00492 
00493     double retryTime;
00494     CClientListener* listener = NULL;
00495     try {
00496         listener   = openClientListener(ARG->m_config->getSynergyAddress());
00497         s_server   = openServer(*ARG->m_config, s_primaryClient);
00498         s_listener = listener;
00499         updateStatus();
00500         LOG((CLOG_NOTE "started server"));
00501         s_serverState = kStarted;
00502         return true;
00503     }
00504     catch (XSocketAddressInUse& e) {
00505         LOG((CLOG_WARN "cannot listen for clients: %s", e.what()));
00506         closeClientListener(listener);
00507         updateStatus(CString("cannot listen for clients: ") + e.what());
00508         retryTime = 10.0;
00509     }
00510     catch (XBase& e) {
00511         LOG((CLOG_CRIT "failed to start server: %s", e.what()));
00512         closeClientListener(listener);
00513         return false;
00514     }
00515 
00516     if (ARG->m_restartable) {
00517         // install a timer and handler to retry later
00518         assert(s_timer == NULL);
00519         LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
00520         s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
00521         EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer,
00522                             new CFunctionEventJob(&retryHandler, NULL));
00523         s_serverState = kStarting;
00524         return true;
00525     }
00526     else {
00527         // don't try again
00528         return false;
00529     }
00530 }
00531 
00532 static
00533 void
00534 stopServer()
00535 {
00536     if (s_serverState == kStarted) {
00537         closeClientListener(s_listener);
00538         closeServer(s_server);
00539         s_server      = NULL;
00540         s_listener    = NULL;
00541         s_serverState = kInitialized;
00542     }
00543     else if (s_serverState == kStarting) {
00544         stopRetryTimer();
00545         s_serverState = kInitialized;
00546     }
00547     assert(s_server == NULL);
00548     assert(s_listener == NULL);
00549 }
00550 
00551 static
00552 void
00553 cleanupServer()
00554 {
00555     stopServer();
00556     if (s_serverState == kInitialized) {
00557         closePrimaryClient(s_primaryClient);
00558         closeServerScreen(s_serverScreen);
00559         s_primaryClient = NULL;
00560         s_serverScreen  = NULL;
00561         s_serverState   = kUninitialized;
00562     }
00563     else if (s_serverState == kInitializing ||
00564             s_serverState == kInitializingToStart) {
00565         stopRetryTimer();
00566         s_serverState = kUninitialized;
00567     }
00568     assert(s_primaryClient == NULL);
00569     assert(s_serverScreen == NULL);
00570     assert(s_serverState == kUninitialized);
00571 }
00572 
00573 static
00574 void
00575 handleSuspend(const CEvent&, void*)
00576 {
00577     if (!s_suspended) {
00578         LOG((CLOG_INFO "suspend"));
00579         stopServer();
00580         s_suspended = true;
00581     }
00582 }
00583 
00584 static
00585 void
00586 handleResume(const CEvent&, void*)
00587 {
00588     if (s_suspended) {
00589         LOG((CLOG_INFO "resume"));
00590         startServer();
00591         s_suspended = false;
00592     }
00593 }
00594 
00595 static
00596 void
00597 reloadSignalHandler(CArch::ESignal, void*)
00598 {
00599     EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(),
00600                             IEventQueue::getSystemTarget()));
00601 }
00602 
00603 static
00604 void
00605 reloadConfig(const CEvent&, void*)
00606 {
00607     LOG((CLOG_DEBUG "reload configuration"));
00608     if (loadConfig(ARG->m_configFile)) {
00609         if (s_server != NULL) {
00610             s_server->setConfig(*ARG->m_config);
00611         }
00612         LOG((CLOG_NOTE "reloaded configuration"));
00613     }
00614 }
00615 
00616 static
00617 void
00618 forceReconnect(const CEvent&, void*)
00619 {
00620     if (s_server != NULL) {
00621         s_server->disconnect();
00622     }
00623 }
00624 
00625 static
00626 int
00627 mainLoop()
00628 {
00629     // create socket multiplexer.  this must happen after daemonization
00630     // on unix because threads evaporate across a fork().
00631     CSocketMultiplexer multiplexer;
00632 
00633     // create the event queue
00634     CEventQueue eventQueue;
00635 
00636     // if configuration has no screens then add this system
00637     // as the default
00638     if (ARG->m_config->begin() == ARG->m_config->end()) {
00639         ARG->m_config->addScreen(ARG->m_name);
00640     }
00641 
00642     // set the contact address, if provided, in the config.
00643     // otherwise, if the config doesn't have an address, use
00644     // the default.
00645     if (ARG->m_synergyAddress->isValid()) {
00646         ARG->m_config->setSynergyAddress(*ARG->m_synergyAddress);
00647     }
00648     else if (!ARG->m_config->getSynergyAddress().isValid()) {
00649         ARG->m_config->setSynergyAddress(CNetworkAddress(kDefaultPort));
00650     }
00651 
00652     // canonicalize the primary screen name
00653     CString primaryName = ARG->m_config->getCanonicalName(ARG->m_name);
00654     if (primaryName.empty()) {
00655         LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str()));
00656         return kExitFailed;
00657     }
00658 
00659     // start the server.  if this return false then we've failed and
00660     // we shouldn't retry.
00661     LOG((CLOG_DEBUG1 "starting server"));
00662     if (!startServer()) {
00663         return kExitFailed;
00664     }
00665 
00666     // handle hangup signal by reloading the server's configuration
00667     ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL);
00668     EVENTQUEUE->adoptHandler(getReloadConfigEvent(),
00669                             IEventQueue::getSystemTarget(),
00670                             new CFunctionEventJob(&reloadConfig));
00671 
00672     // handle force reconnect event by disconnecting clients.  they'll
00673     // reconnect automatically.
00674     EVENTQUEUE->adoptHandler(getForceReconnectEvent(),
00675                             IEventQueue::getSystemTarget(),
00676                             new CFunctionEventJob(&forceReconnect));
00677 
00678     // run event loop.  if startServer() failed we're supposed to retry
00679     // later.  the timer installed by startServer() will take care of
00680     // that.
00681     CEvent event;
00682     DAEMON_RUNNING(true);
00683     EVENTQUEUE->getEvent(event);
00684     while (event.getType() != CEvent::kQuit) {
00685         EVENTQUEUE->dispatchEvent(event);
00686         CEvent::deleteData(event);
00687         EVENTQUEUE->getEvent(event);
00688     }
00689     DAEMON_RUNNING(false);
00690 
00691     // close down
00692     LOG((CLOG_DEBUG1 "stopping server"));
00693     EVENTQUEUE->removeHandler(getForceReconnectEvent(),
00694                             IEventQueue::getSystemTarget());
00695     EVENTQUEUE->removeHandler(getReloadConfigEvent(),
00696                             IEventQueue::getSystemTarget());
00697     cleanupServer();
00698     updateStatus();
00699     LOG((CLOG_NOTE "stopped server"));
00700 
00701     return kExitSuccess;
00702 }
00703 
00704 static
00705 int
00706 daemonMainLoop(int, const char**)
00707 {
00708 #if SYSAPI_WIN32
00709     CSystemLogger sysLogger(DAEMON_NAME, false);
00710 #else
00711     CSystemLogger sysLogger(DAEMON_NAME, true);
00712 #endif
00713     return mainLoop();
00714 }
00715 
00716 static
00717 int
00718 standardStartup(int argc, char** argv)
00719 {
00720 
00721     // parse command line
00722     parse(argc, argv);
00723 
00724     // load configuration
00725     loadConfig();
00726 
00727     // daemonize if requested
00728     if (ARG->m_daemon) {
00729         return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop);
00730     }
00731     else {
00732         return mainLoop();
00733     }
00734 }
00735 
00736 static
00737 int
00738 run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
00739 {
00740     // general initialization
00741     ARG->m_synergyAddress = new CNetworkAddress;
00742     ARG->m_config         = new CConfig;
00743     ARG->m_pname          = ARCH->getBasename(argv[0]);
00744 
00745     // install caller's output filter
00746     if (outputter != NULL) {
00747         CLOG->insert(outputter);
00748     }
00749 
00750     // save log messages
00751     CBufferedLogOutputter logBuffer(1000);
00752     CLOG->insert(&logBuffer, true);
00753 
00754     // make the task bar receiver.  the user can control this app
00755     // through the task bar.
00756     s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
00757 
00758     // run
00759     int result = startup(argc, argv);
00760 
00761     // done with task bar receiver
00762     delete s_taskBarReceiver;
00763 
00764     // done with log buffer
00765     CLOG->remove(&logBuffer);
00766 
00767     delete ARG->m_config;
00768     delete ARG->m_synergyAddress;
00769     return result;
00770 }
00771 
00772 
00773 //
00774 // command line parsing
00775 //
00776 
00777 #define BYE "\nTry `%s --help' for more information."
00778 
00779 static void             (*bye)(int) = &exit;
00780 
00781 static
00782 void
00783 version()
00784 {
00785     LOG((CLOG_PRINT
00786 "%s %s, protocol version %d.%d\n"
00787 "%s",
00788                                 ARG->m_pname,
00789                                 kVersion,
00790                                 kProtocolMajorVersion,
00791                                 kProtocolMinorVersion,
00792                                 kCopyright));
00793 }
00794 
00795 static
00796 void
00797 help()
00798 {
00799 #if WINAPI_XWINDOWS
00800 #  define USAGE_DISPLAY_ARG     \
00801 " [--display <display>]"
00802 #  define USAGE_DISPLAY_INFO    \
00803 "      --display <display>  connect to the X server at <display>\n"
00804 #else
00805 #  define USAGE_DISPLAY_ARG
00806 #  define USAGE_DISPLAY_INFO
00807 #endif
00808 
00809 #if SYSAPI_WIN32
00810 
00811 #  define PLATFORM_ARGS                                                     \
00812 " [--daemon|--no-daemon]"
00813 #  define PLATFORM_DESC
00814 #  define PLATFORM_EXTRA                                                    \
00815 "At least one command line argument is required.  If you don't otherwise\n" \
00816 "need an argument use `--daemon'.\n"                                        \
00817 "\n"
00818 
00819 #else
00820 
00821 #  define PLATFORM_ARGS                                                     \
00822 " [--daemon|--no-daemon]"
00823 #  define PLATFORM_DESC
00824 #  define PLATFORM_EXTRA
00825 
00826 #endif
00827 
00828     LOG((CLOG_PRINT
00829 "Usage: %s"
00830 " [--address <address>]"
00831 " [--config <pathname>]"
00832 " [--debug <level>]"
00833 USAGE_DISPLAY_ARG
00834 " [--name <screen-name>]"
00835 " [--restart|--no-restart]"
00836 PLATFORM_ARGS
00837 "\n\n"
00838 "Start the synergy mouse/keyboard sharing server.\n"
00839 "\n"
00840 "  -a, --address <address>  listen for clients on the given address.\n"
00841 "  -c, --config <pathname>  use the named configuration file instead.\n"
00842 "  -d, --debug <level>      filter out log messages with priorty below level.\n"
00843 "                           level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
00844 "                           DEBUG, DEBUG1, DEBUG2.\n"
00845 USAGE_DISPLAY_INFO
00846 "  -f, --no-daemon          run the server in the foreground.\n"
00847 "*     --daemon             run the server as a daemon.\n"
00848 "  -n, --name <screen-name> use screen-name instead the hostname to identify\n"
00849 "                           this screen in the configuration.\n"
00850 "  -1, --no-restart         do not try to restart the server if it fails for\n"
00851 "                           some reason.\n"
00852 "*     --restart            restart the server automatically if it fails.\n"
00853 PLATFORM_DESC
00854 "  -h, --help               display this help and exit.\n"
00855 "      --version            display version information and exit.\n"
00856 "\n"
00857 "* marks defaults.\n"
00858 "\n"
00859 PLATFORM_EXTRA
00860 "The argument for --address is of the form: [<hostname>][:<port>].  The\n"
00861 "hostname must be the address or hostname of an interface on the system.\n"
00862 "The default is to listen on all interfaces.  The port overrides the\n"
00863 "default port, %d.\n"
00864 "\n"
00865 "If no configuration file pathname is provided then the first of the\n"
00866 "following to load successfully sets the configuration:\n"
00867 "  %s\n"
00868 "  %s\n"
00869 "If no configuration file can be loaded then the configuration uses its\n"
00870 "defaults with just the server screen.\n"
00871 "\n"
00872 "Where log messages go depends on the platform and whether or not the\n"
00873 "server is running as a daemon.",
00874                                 ARG->m_pname,
00875                                 kDefaultPort,
00876                                 ARCH->concatPath(
00877                                     ARCH->getUserDirectory(),
00878                                     USR_CONFIG_NAME).c_str(),
00879                                 ARCH->concatPath(
00880                                     ARCH->getSystemDirectory(),
00881                                     SYS_CONFIG_NAME).c_str()));
00882 }
00883 
00884 static
00885 bool
00886 isArg(int argi, int argc, const char* const* argv,
00887                 const char* name1, const char* name2,
00888                 int minRequiredParameters = 0)
00889 {
00890     if ((name1 != NULL && strcmp(argv[argi], name1) == 0) ||
00891         (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
00892         // match.  check args left.
00893         if (argi + minRequiredParameters >= argc) {
00894             LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
00895                                 ARG->m_pname, argv[argi], ARG->m_pname));
00896             bye(kExitArgs);
00897         }
00898         return true;
00899     }
00900 
00901     // no match
00902     return false;
00903 }
00904 
00905 static
00906 void
00907 parse(int argc, const char* const* argv)
00908 {
00909     assert(ARG->m_pname != NULL);
00910     assert(argv       != NULL);
00911     assert(argc       >= 1);
00912 
00913     // set defaults
00914     ARG->m_name = ARCH->getHostName();
00915 
00916     // parse options
00917     int i = 1;
00918     for (; i < argc; ++i) {
00919         if (isArg(i, argc, argv, "-d", "--debug", 1)) {
00920             // change logging level
00921             ARG->m_logFilter = argv[++i];
00922         }
00923 
00924         else if (isArg(i, argc, argv, "-a", "--address", 1)) {
00925             // save listen address
00926             try {
00927                 *ARG->m_synergyAddress = CNetworkAddress(argv[i + 1],
00928                                                         kDefaultPort);
00929                 ARG->m_synergyAddress->resolve();
00930             }
00931             catch (XSocketAddress& e) {
00932                 LOG((CLOG_PRINT "%s: %s" BYE,
00933                                 ARG->m_pname, e.what(), ARG->m_pname));
00934                 bye(kExitArgs);
00935             }
00936             ++i;
00937         }
00938 
00939         else if (isArg(i, argc, argv, "-n", "--name", 1)) {
00940             // save screen name
00941             ARG->m_name = argv[++i];
00942         }
00943 
00944         else if (isArg(i, argc, argv, "-c", "--config", 1)) {
00945             // save configuration file path
00946             ARG->m_configFile = argv[++i];
00947         }
00948 
00949 #if WINAPI_XWINDOWS
00950         else if (isArg(i, argc, argv, "-display", "--display", 1)) {
00951             // use alternative display
00952             ARG->m_display = argv[++i];
00953         }
00954 #endif
00955 
00956         else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
00957             // not a daemon
00958             ARG->m_daemon = false;
00959         }
00960 
00961         else if (isArg(i, argc, argv, NULL, "--daemon")) {
00962             // daemonize
00963             ARG->m_daemon = true;
00964         }
00965 
00966         else if (isArg(i, argc, argv, "-1", "--no-restart")) {
00967             // don't try to restart
00968             ARG->m_restartable = false;
00969         }
00970 
00971         else if (isArg(i, argc, argv, NULL, "--restart")) {
00972             // try to restart
00973             ARG->m_restartable = true;
00974         }
00975 
00976         else if (isArg(i, argc, argv, "-z", NULL)) {
00977             ARG->m_backend = true;
00978         }
00979 
00980         else if (isArg(i, argc, argv, "-h", "--help")) {
00981             help();
00982             bye(kExitSuccess);
00983         }
00984 
00985         else if (isArg(i, argc, argv, NULL, "--version")) {
00986             version();
00987             bye(kExitSuccess);
00988         }
00989 
00990         else if (isArg(i, argc, argv, "--", NULL)) {
00991             // remaining arguments are not options
00992             ++i;
00993             break;
00994         }
00995 
00996         else if (argv[i][0] == '-') {
00997             LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
00998                                 ARG->m_pname, argv[i], ARG->m_pname));
00999             bye(kExitArgs);
01000         }
01001 
01002         else {
01003             // this and remaining arguments are not options
01004             break;
01005         }
01006     }
01007 
01008     // no non-option arguments are allowed
01009     if (i != argc) {
01010         LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
01011                                 ARG->m_pname, argv[i], ARG->m_pname));
01012         bye(kExitArgs);
01013     }
01014 
01015     // increase default filter level for daemon.  the user must
01016     // explicitly request another level for a daemon.
01017     if (ARG->m_daemon && ARG->m_logFilter == NULL) {
01018 #if SYSAPI_WIN32
01019         if (CArchMiscWindows::isWindows95Family()) {
01020             // windows 95 has no place for logging so avoid showing
01021             // the log console window.
01022             ARG->m_logFilter = "FATAL";
01023         }
01024         else
01025 #endif
01026         {
01027             ARG->m_logFilter = "NOTE";
01028         }
01029     }
01030 
01031     // set log filter
01032     if (!CLOG->setFilter(ARG->m_logFilter)) {
01033         LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
01034                                 ARG->m_pname, ARG->m_logFilter, ARG->m_pname));
01035         bye(kExitArgs);
01036     }
01037 
01038     // identify system
01039     LOG((CLOG_INFO "%s Server on %s %s", kAppVersion, ARCH->getOSName().c_str(), ARCH->getPlatformName().c_str()));
01040 
01041 #ifdef WIN32
01042 #ifdef _AMD64_
01043     LOG((CLOG_WARN "This is an experimental x64 build of %s. Use it at your own risk.", kApplication));
01044 #endif
01045 #endif
01046 }
01047 
01048 static
01049 bool
01050 loadConfig(const CString& pathname)
01051 {
01052     try {
01053         // load configuration
01054         LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str()));
01055         std::ifstream configStream(pathname.c_str());
01056         if (!configStream.is_open()) {
01057             // report failure to open configuration as a debug message
01058             // since we try several paths and we expect some to be
01059             // missing.
01060             LOG((CLOG_DEBUG "cannot open configuration \"%s\"",
01061                                 pathname.c_str()));
01062             return false;
01063         }
01064         configStream >> *ARG->m_config;
01065         LOG((CLOG_DEBUG "configuration read successfully"));
01066         return true;
01067     }
01068     catch (XConfigRead& e) {
01069         // report error in configuration file
01070         LOG((CLOG_ERR "cannot read configuration \"%s\": %s",
01071                                 pathname.c_str(), e.what()));
01072     }
01073     return false;
01074 }
01075 
01076 static
01077 void
01078 loadConfig()
01079 {
01080     bool loaded = false;
01081 
01082     // load the config file, if specified
01083     if (!ARG->m_configFile.empty()) {
01084         loaded = loadConfig(ARG->m_configFile);
01085     }
01086 
01087     // load the default configuration if no explicit file given
01088     else {
01089         // get the user's home directory
01090         CString path = ARCH->getUserDirectory();
01091         if (!path.empty()) {
01092             // complete path
01093             path = ARCH->concatPath(path, USR_CONFIG_NAME);
01094 
01095             // now try loading the user's configuration
01096             if (loadConfig(path)) {
01097                 loaded            = true;
01098                 ARG->m_configFile = path;
01099             }
01100         }
01101         if (!loaded) {
01102             // try the system-wide config file
01103             path = ARCH->getSystemDirectory();
01104             if (!path.empty()) {
01105                 path = ARCH->concatPath(path, SYS_CONFIG_NAME);
01106                 if (loadConfig(path)) {
01107                     loaded            = true;
01108                     ARG->m_configFile = path;
01109                 }
01110             }
01111         }
01112     }
01113 
01114     if (!loaded) {
01115         LOG((CLOG_PRINT "%s: no configuration available", ARG->m_pname));
01116         bye(kExitConfig);
01117     }
01118 }
01119 
01120 
01121 //
01122 // platform dependent entry points
01123 //
01124 
01125 #if SYSAPI_WIN32
01126 
01127 static bool             s_hasImportantLogMessages = false;
01128 
01129 //
01130 // CMessageBoxOutputter
01131 //
01132 // This class writes severe log messages to a message box
01133 //
01134 
01135 class CMessageBoxOutputter : public ILogOutputter {
01136 public:
01137     CMessageBoxOutputter() { }
01138     virtual ~CMessageBoxOutputter() { }
01139 
01140     // ILogOutputter overrides
01141     virtual void        open(const char*) { }
01142     virtual void        close() { }
01143     virtual void        show(bool) { }
01144     virtual bool        write(ELevel level, const char* message);
01145     virtual const char* getNewline() const { return ""; }
01146 };
01147 
01148 bool
01149 CMessageBoxOutputter::write(ELevel level, const char* message)
01150 {
01151     // note any important messages the user may need to know about
01152     if (level <= CLog::kWARNING) {
01153         s_hasImportantLogMessages = true;
01154     }
01155 
01156     // FATAL and PRINT messages get a dialog box if not running as
01157     // backend.  if we're running as a backend the user will have
01158     // a chance to see the messages when we exit.
01159     if (!ARG->m_backend && level <= CLog::kFATAL) {
01160         MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING);
01161         return false;
01162     }
01163     else {
01164         return true;
01165     }
01166 }
01167 
01168 static
01169 void
01170 byeThrow(int x)
01171 {
01172     CArchMiscWindows::daemonFailed(x);
01173 }
01174 
01175 static
01176 int
01177 daemonNTMainLoop(int argc, const char** argv)
01178 {
01179     parse(argc, argv);
01180     ARG->m_backend = false;
01181     loadConfig();
01182     return CArchMiscWindows::runDaemon(mainLoop);
01183 }
01184 
01185 static
01186 int
01187 daemonNTStartup(int, char**)
01188 {
01189     CSystemLogger sysLogger(DAEMON_NAME, false);
01190     bye = &byeThrow;
01191     return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop);
01192 }
01193 
01194 static
01195 int
01196 foregroundStartup(int argc, char** argv)
01197 {
01198 
01199     // parse command line
01200     parse(argc, argv);
01201 
01202     // load configuration
01203     loadConfig();
01204 
01205     // never daemonize
01206     return mainLoop();
01207 }
01208 
01209 static
01210 void
01211 showError(HINSTANCE instance, const char* title, UINT id, const char* arg)
01212 {
01213     CString fmt = CMSWindowsUtil::getString(instance, id);
01214     CString msg = CStringUtil::format(fmt.c_str(), arg);
01215     MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING);
01216 }
01217 
01218 int WINAPI
01219 WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
01220 {
01221     try {
01222         CArchMiscWindows::setIcons((HICON)LoadImage(instance,
01223                                     MAKEINTRESOURCE(IDI_SYNERGY),
01224                                     IMAGE_ICON,
01225                                     32, 32, LR_SHARED),
01226                                     (HICON)LoadImage(instance,
01227                                     MAKEINTRESOURCE(IDI_SYNERGY),
01228                                     IMAGE_ICON,
01229                                     16, 16, LR_SHARED));
01230         CArch arch(instance);
01231         CMSWindowsScreen::init(instance);
01232         CLOG;
01233         CThread::getCurrentThread().setPriority(-14);
01234         CArgs args;
01235 
01236         // set title on log window
01237         ARCH->openConsole((CString(kAppVersion) + " " + "Server").c_str());
01238 
01239         // windows NT family starts services using no command line options.
01240         // since i'm not sure how to tell the difference between that and
01241         // a user providing no options we'll assume that if there are no
01242         // arguments and we're on NT then we're being invoked as a service.
01243         // users on NT can use `--daemon' or `--no-daemon' to force us out
01244         // of the service code path.
01245         StartupFunc startup = &standardStartup;
01246         if (!CArchMiscWindows::isWindows95Family()) {
01247             if (__argc <= 1) {
01248                 startup = &daemonNTStartup;
01249             }
01250             else {
01251                 startup = &foregroundStartup;
01252             }
01253         }
01254 
01255         // send PRINT and FATAL output to a message box
01256         int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
01257 
01258         // let user examine any messages if we're running as a backend
01259         // by putting up a dialog box before exiting.
01260         if (args.m_backend && s_hasImportantLogMessages) {
01261             showError(instance, args.m_pname, IDS_FAILED, "");
01262         }
01263 
01264         delete CLOG;
01265         return result;
01266     }
01267     catch (XBase& e) {
01268         showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what());
01269         //throw;
01270     }
01271     catch (XArch& e) {
01272         showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str());
01273     }
01274     catch (...) {
01275         showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, "<unknown>");
01276         //throw;
01277     }
01278     return kExitFailed;
01279 }
01280 
01281 #elif SYSAPI_UNIX
01282 
01283 int
01284 main(int argc, char** argv)
01285 {
01286     CArgs args;
01287     try {
01288         int result;
01289         CArch arch;
01290         CLOG;
01291         CArgs args;
01292         result = run(argc, argv, NULL, &standardStartup);
01293         delete CLOG;
01294         return result;
01295     }
01296     catch (XBase& e) {
01297         LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what()));
01298         throw;
01299     }
01300     catch (XArch& e) {
01301         LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str()));
01302         return kExitFailed;
01303     }
01304     catch (...) {
01305         LOG((CLOG_CRIT "Uncaught exception: <unknown exception>\n"));
01306         throw;
01307     }
01308 }
01309 
01310 #else
01311 
01312 #error no main() for platform
01313 
01314 #endif

Generated on Fri Nov 6 00:21:15 2009 for synergy-plus by  doxygen 1.3.9.1