00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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
00057 #if SYSAPI_WIN32
00058 #define DAEMON_NAME "Synergy Server"
00059 #elif SYSAPI_UNIX
00060 #define DAEMON_NAME "synergys"
00061 #endif
00062
00063
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
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
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
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
00326 server->disconnect();
00327
00328
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
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
00369 assert(s_timer != NULL);
00370 stopRetryTimer();
00371
00372
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
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
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
00466 return false;
00467 }
00468 }
00469
00470 static
00471 bool
00472 startServer()
00473 {
00474
00475 if (s_serverState == kStarting || s_serverState == kStarted) {
00476 return true;
00477 }
00478
00479
00480 if (s_serverState != kInitialized) {
00481 if (!initServer()) {
00482
00483 return false;
00484 }
00485 if (s_serverState == kInitializing) {
00486
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
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
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
00630
00631 CSocketMultiplexer multiplexer;
00632
00633
00634 CEventQueue eventQueue;
00635
00636
00637
00638 if (ARG->m_config->begin() == ARG->m_config->end()) {
00639 ARG->m_config->addScreen(ARG->m_name);
00640 }
00641
00642
00643
00644
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
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
00660
00661 LOG((CLOG_DEBUG1 "starting server"));
00662 if (!startServer()) {
00663 return kExitFailed;
00664 }
00665
00666
00667 ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL);
00668 EVENTQUEUE->adoptHandler(getReloadConfigEvent(),
00669 IEventQueue::getSystemTarget(),
00670 new CFunctionEventJob(&reloadConfig));
00671
00672
00673
00674 EVENTQUEUE->adoptHandler(getForceReconnectEvent(),
00675 IEventQueue::getSystemTarget(),
00676 new CFunctionEventJob(&forceReconnect));
00677
00678
00679
00680
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
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
00722 parse(argc, argv);
00723
00724
00725 loadConfig();
00726
00727
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
00741 ARG->m_synergyAddress = new CNetworkAddress;
00742 ARG->m_config = new CConfig;
00743 ARG->m_pname = ARCH->getBasename(argv[0]);
00744
00745
00746 if (outputter != NULL) {
00747 CLOG->insert(outputter);
00748 }
00749
00750
00751 CBufferedLogOutputter logBuffer(1000);
00752 CLOG->insert(&logBuffer, true);
00753
00754
00755
00756 s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
00757
00758
00759 int result = startup(argc, argv);
00760
00761
00762 delete s_taskBarReceiver;
00763
00764
00765 CLOG->remove(&logBuffer);
00766
00767 delete ARG->m_config;
00768 delete ARG->m_synergyAddress;
00769 return result;
00770 }
00771
00772
00773
00774
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
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
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
00914 ARG->m_name = ARCH->getHostName();
00915
00916
00917 int i = 1;
00918 for (; i < argc; ++i) {
00919 if (isArg(i, argc, argv, "-d", "--debug", 1)) {
00920
00921 ARG->m_logFilter = argv[++i];
00922 }
00923
00924 else if (isArg(i, argc, argv, "-a", "--address", 1)) {
00925
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
00941 ARG->m_name = argv[++i];
00942 }
00943
00944 else if (isArg(i, argc, argv, "-c", "--config", 1)) {
00945
00946 ARG->m_configFile = argv[++i];
00947 }
00948
00949 #if WINAPI_XWINDOWS
00950 else if (isArg(i, argc, argv, "-display", "--display", 1)) {
00951
00952 ARG->m_display = argv[++i];
00953 }
00954 #endif
00955
00956 else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
00957
00958 ARG->m_daemon = false;
00959 }
00960
00961 else if (isArg(i, argc, argv, NULL, "--daemon")) {
00962
00963 ARG->m_daemon = true;
00964 }
00965
00966 else if (isArg(i, argc, argv, "-1", "--no-restart")) {
00967
00968 ARG->m_restartable = false;
00969 }
00970
00971 else if (isArg(i, argc, argv, NULL, "--restart")) {
00972
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
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
01004 break;
01005 }
01006 }
01007
01008
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
01016
01017 if (ARG->m_daemon && ARG->m_logFilter == NULL) {
01018 #if SYSAPI_WIN32
01019 if (CArchMiscWindows::isWindows95Family()) {
01020
01021
01022 ARG->m_logFilter = "FATAL";
01023 }
01024 else
01025 #endif
01026 {
01027 ARG->m_logFilter = "NOTE";
01028 }
01029 }
01030
01031
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
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
01054 LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str()));
01055 std::ifstream configStream(pathname.c_str());
01056 if (!configStream.is_open()) {
01057
01058
01059
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
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
01083 if (!ARG->m_configFile.empty()) {
01084 loaded = loadConfig(ARG->m_configFile);
01085 }
01086
01087
01088 else {
01089
01090 CString path = ARCH->getUserDirectory();
01091 if (!path.empty()) {
01092
01093 path = ARCH->concatPath(path, USR_CONFIG_NAME);
01094
01095
01096 if (loadConfig(path)) {
01097 loaded = true;
01098 ARG->m_configFile = path;
01099 }
01100 }
01101 if (!loaded) {
01102
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
01123
01124
01125 #if SYSAPI_WIN32
01126
01127 static bool s_hasImportantLogMessages = false;
01128
01129
01130
01131
01132
01133
01134
01135 class CMessageBoxOutputter : public ILogOutputter {
01136 public:
01137 CMessageBoxOutputter() { }
01138 virtual ~CMessageBoxOutputter() { }
01139
01140
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
01152 if (level <= CLog::kWARNING) {
01153 s_hasImportantLogMessages = true;
01154 }
01155
01156
01157
01158
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
01200 parse(argc, argv);
01201
01202
01203 loadConfig();
01204
01205
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
01237 ARCH->openConsole((CString(kAppVersion) + " " + "Server").c_str());
01238
01239
01240
01241
01242
01243
01244
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
01256 int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
01257
01258
01259
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
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
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