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

synergyc.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 "CClient.h"
00016 #include "CScreen.h"
00017 #include "ProtocolTypes.h"
00018 #include "Version.h"
00019 #include "XScreen.h"
00020 #include "CNetworkAddress.h"
00021 #include "CSocketMultiplexer.h"
00022 #include "CTCPSocketFactory.h"
00023 #include "XSocket.h"
00024 #include "CThread.h"
00025 #include "CEventQueue.h"
00026 #include "CFunctionEventJob.h"
00027 #include "CFunctionJob.h"
00028 #include "CLog.h"
00029 #include "CString.h"
00030 #include "CStringUtil.h"
00031 #include "LogOutputters.h"
00032 #include "CArch.h"
00033 #include "XArch.h"
00034 #include <cstring>
00035 
00036 #define DAEMON_RUNNING(running_)
00037 #if WINAPI_MSWINDOWS
00038 #include "CArchMiscWindows.h"
00039 #include "CMSWindowsScreen.h"
00040 #include "CMSWindowsUtil.h"
00041 #include "CMSWindowsClientTaskBarReceiver.h"
00042 #include "resource.h"
00043 #undef DAEMON_RUNNING
00044 #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
00045 #elif WINAPI_XWINDOWS
00046 #include "CXWindowsScreen.h"
00047 #include "CXWindowsClientTaskBarReceiver.h"
00048 #elif WINAPI_CARBON
00049 #include "COSXScreen.h"
00050 #include "COSXClientTaskBarReceiver.h"
00051 #endif
00052 
00053 // platform dependent name of a daemon
00054 #if SYSAPI_WIN32
00055 #define DAEMON_NAME "Synergy Client"
00056 #elif SYSAPI_UNIX
00057 #define DAEMON_NAME "synergyc"
00058 #endif
00059 
00060 typedef int (*StartupFunc)(int, char**);
00061 static bool startClient();
00062 static void parse(int argc, const char* const* argv);
00063 
00064 //
00065 // program arguments
00066 //
00067 
00068 #define ARG CArgs::s_instance
00069 
00070 class CArgs {
00071 public:
00072     CArgs() :
00073         m_pname(NULL),
00074         m_backend(false),
00075         m_restartable(true),
00076         m_daemon(true),
00077         m_yscroll(0),
00078         m_logFilter(NULL),
00079         m_display(NULL),
00080         m_serverAddress(NULL)
00081         { s_instance = this; }
00082     ~CArgs() { s_instance = NULL; }
00083 
00084 public:
00085     static CArgs*       s_instance;
00086     const char*         m_pname;
00087     bool                m_backend;
00088     bool                m_restartable;
00089     bool                m_daemon;
00090     int                 m_yscroll;
00091     const char*         m_logFilter;
00092     const char*         m_display;
00093     CString             m_name;
00094     CNetworkAddress*    m_serverAddress;
00095 };
00096 
00097 CArgs*                  CArgs::s_instance = NULL;
00098 
00099 
00100 //
00101 // platform dependent factories
00102 //
00103 
00104 static
00105 CScreen*
00106 createScreen()
00107 {
00108 #if WINAPI_MSWINDOWS
00109     return new CScreen(new CMSWindowsScreen(false));
00110 #elif WINAPI_XWINDOWS
00111     return new CScreen(new CXWindowsScreen(ARG->m_display, false, ARG->m_yscroll));
00112 #elif WINAPI_CARBON
00113     return new CScreen(new COSXScreen(false));
00114 #endif
00115 }
00116 
00117 static
00118 CClientTaskBarReceiver*
00119 createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
00120 {
00121 #if WINAPI_MSWINDOWS
00122     return new CMSWindowsClientTaskBarReceiver(
00123                             CMSWindowsScreen::getInstance(), logBuffer);
00124 #elif WINAPI_XWINDOWS
00125     return new CXWindowsClientTaskBarReceiver(logBuffer);
00126 #elif WINAPI_CARBON
00127     return new COSXClientTaskBarReceiver(logBuffer);
00128 #endif
00129 }
00130 
00131 
00132 //
00133 // platform independent main
00134 //
00135 
00136 static CClient*                 s_client          = NULL;
00137 static CScreen*                 s_clientScreen    = NULL;
00138 static CClientTaskBarReceiver*  s_taskBarReceiver = NULL;
00139 //static double                 s_retryTime       = 0.0;
00140 static bool                     s_suspened        = false;
00141 
00142 #define RETRY_TIME 1.0
00143 
00144 static
00145 void
00146 updateStatus()
00147 {
00148     s_taskBarReceiver->updateStatus(s_client, "");
00149 }
00150 
00151 static
00152 void
00153 updateStatus(const CString& msg)
00154 {
00155     s_taskBarReceiver->updateStatus(s_client, msg);
00156 }
00157 
00158 static
00159 void
00160 resetRestartTimeout()
00161 {
00162     // retry time can nolonger be changed
00163     //s_retryTime = 0.0;
00164 }
00165 
00166 static
00167 double
00168 nextRestartTimeout()
00169 {
00170     // retry at a constant rate (Issue 52)
00171     return RETRY_TIME;
00172 
00173     /*
00174     // choose next restart timeout.  we start with rapid retries
00175     // then slow down.
00176     if (s_retryTime < 1.0) {
00177         s_retryTime = 1.0;
00178     }
00179     else if (s_retryTime < 3.0) {
00180         s_retryTime = 3.0;
00181     }
00182     else {
00183         s_retryTime = 5.0;
00184     }
00185     return s_retryTime;
00186     */
00187 }
00188 
00189 static
00190 void
00191 handleScreenError(const CEvent&, void*)
00192 {
00193     LOG((CLOG_CRIT "error on screen"));
00194     EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00195 }
00196 
00197 static
00198 CScreen*
00199 openClientScreen()
00200 {
00201     CScreen* screen = createScreen();
00202     EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
00203                             screen->getEventTarget(),
00204                             new CFunctionEventJob(
00205                                 &handleScreenError));
00206     return screen;
00207 }
00208 
00209 static
00210 void
00211 closeClientScreen(CScreen* screen)
00212 {
00213     if (screen != NULL) {
00214         EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
00215                             screen->getEventTarget());
00216         delete screen;
00217     }
00218 }
00219 
00220 static
00221 void
00222 handleClientRestart(const CEvent&, void* vtimer)
00223 {
00224     // discard old timer
00225     CEventQueueTimer* timer = reinterpret_cast<CEventQueueTimer*>(vtimer);
00226     EVENTQUEUE->deleteTimer(timer);
00227     EVENTQUEUE->removeHandler(CEvent::kTimer, timer);
00228 
00229     // reconnect
00230     startClient();
00231 }
00232 
00233 static
00234 void
00235 scheduleClientRestart(double retryTime)
00236 {
00237     // install a timer and handler to retry later
00238     LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
00239     CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
00240     EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
00241                             new CFunctionEventJob(&handleClientRestart, timer));
00242 }
00243 
00244 static
00245 void
00246 handleClientConnected(const CEvent&, void*)
00247 {
00248     LOG((CLOG_NOTE "connected to server"));
00249     resetRestartTimeout();
00250     updateStatus();
00251 }
00252 
00253 static
00254 void
00255 handleClientFailed(const CEvent& e, void*)
00256 {
00257     CClient::CFailInfo* info =
00258         reinterpret_cast<CClient::CFailInfo*>(e.getData());
00259 
00260     updateStatus(CString("Failed to connect to server: ") + info->m_what);
00261     if (!ARG->m_restartable || !info->m_retry) {
00262         LOG((CLOG_ERR "failed to connect to server: %s", info->m_what));
00263         EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00264     }
00265     else {
00266         LOG((CLOG_WARN "failed to connect to server: %s", info->m_what));
00267         if (!s_suspened) {
00268             scheduleClientRestart(nextRestartTimeout());
00269         }
00270     }
00271 }
00272 
00273 static
00274 void
00275 handleClientDisconnected(const CEvent&, void*)
00276 {
00277     LOG((CLOG_NOTE "disconnected from server"));
00278     if (!ARG->m_restartable) {
00279         EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00280     }
00281     else if (!s_suspened) {
00282         s_client->connect();
00283     }
00284     updateStatus();
00285 }
00286 
00287 static
00288 CClient*
00289 openClient(const CString& name, const CNetworkAddress& address, CScreen* screen)
00290 {
00291     CClient* client = new CClient(name, address,
00292                         new CTCPSocketFactory, NULL, screen);
00293     EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(),
00294                         client->getEventTarget(),
00295                         new CFunctionEventJob(handleClientConnected));
00296     EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(),
00297                         client->getEventTarget(),
00298                         new CFunctionEventJob(handleClientFailed));
00299     EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(),
00300                         client->getEventTarget(),
00301                         new CFunctionEventJob(handleClientDisconnected));
00302     return client;
00303 }
00304 
00305 static
00306 void
00307 closeClient(CClient* client)
00308 {
00309     if (client == NULL) {
00310         return;
00311     }
00312 
00313     EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client);
00314     EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client);
00315     EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client);
00316     delete client;
00317 }
00318 
00319 static
00320 bool
00321 startClient()
00322 {
00323     double retryTime;
00324     CScreen* clientScreen = NULL;
00325     try {
00326         if (s_clientScreen == NULL) {
00327             clientScreen = openClientScreen();
00328             s_client     = openClient(ARG->m_name,
00329                             *ARG->m_serverAddress, clientScreen);
00330             s_clientScreen  = clientScreen;
00331             LOG((CLOG_NOTE "started client"));
00332         }
00333         s_client->connect();
00334         updateStatus();
00335         return true;
00336     }
00337     catch (XScreenUnavailable& e) {
00338         LOG((CLOG_WARN "cannot open secondary screen: %s", e.what()));
00339         closeClientScreen(clientScreen);
00340         updateStatus(CString("Cannot open secondary screen: ") + e.what());
00341         retryTime = e.getRetryTime();
00342     }
00343     catch (XScreenOpenFailure& e) {
00344         LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what()));
00345         closeClientScreen(clientScreen);
00346         return false;
00347     }
00348     catch (XBase& e) {
00349         LOG((CLOG_CRIT "failed to start client: %s", e.what()));
00350         closeClientScreen(clientScreen);
00351         return false;
00352     }
00353 
00354     if (ARG->m_restartable) {
00355         scheduleClientRestart(retryTime);
00356         return true;
00357     }
00358     else {
00359         // don't try again
00360         return false;
00361     }
00362 }
00363 
00364 static
00365 void
00366 stopClient()
00367 {
00368     closeClient(s_client);
00369     closeClientScreen(s_clientScreen);
00370     s_client       = NULL;
00371     s_clientScreen = NULL;
00372 }
00373 
00374 static
00375 int
00376 mainLoop()
00377 {
00378     // create socket multiplexer.  this must happen after daemonization
00379     // on unix because threads evaporate across a fork().
00380     CSocketMultiplexer multiplexer;
00381 
00382     // create the event queue
00383     CEventQueue eventQueue;
00384 
00385     // start the client.  if this return false then we've failed and
00386     // we shouldn't retry.
00387     LOG((CLOG_DEBUG1 "starting client"));
00388     if (!startClient()) {
00389         return kExitFailed;
00390     }
00391 
00392     // run event loop.  if startClient() failed we're supposed to retry
00393     // later.  the timer installed by startClient() will take care of
00394     // that.
00395     CEvent event;
00396     DAEMON_RUNNING(true);
00397     EVENTQUEUE->getEvent(event);
00398     while (event.getType() != CEvent::kQuit) {
00399         EVENTQUEUE->dispatchEvent(event);
00400         CEvent::deleteData(event);
00401         EVENTQUEUE->getEvent(event);
00402     }
00403     DAEMON_RUNNING(false);
00404 
00405     // close down
00406     LOG((CLOG_DEBUG1 "stopping client"));
00407     stopClient();
00408     updateStatus();
00409     LOG((CLOG_NOTE "stopped client"));
00410 
00411     return kExitSuccess;
00412 }
00413 
00414 static
00415 int
00416 daemonMainLoop(int, const char**)
00417 {
00418 #if SYSAPI_WIN32
00419     CSystemLogger sysLogger(DAEMON_NAME, false);
00420 #else
00421     CSystemLogger sysLogger(DAEMON_NAME, true);
00422 #endif
00423     return mainLoop();
00424 }
00425 
00426 static
00427 int
00428 standardStartup(int argc, char** argv)
00429 {
00430     if (!ARG->m_daemon) {
00431         ARCH->showConsole(false);
00432     }
00433 
00434     // parse command line
00435     parse(argc, argv);
00436 
00437     // daemonize if requested
00438     if (ARG->m_daemon) {
00439         return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop);
00440     }
00441     else {
00442         return mainLoop();
00443     }
00444 }
00445 
00446 static
00447 int
00448 run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
00449 {
00450     // general initialization
00451     ARG->m_serverAddress = new CNetworkAddress;
00452     ARG->m_pname         = ARCH->getBasename(argv[0]);
00453 
00454     // install caller's output filter
00455     if (outputter != NULL) {
00456         CLOG->insert(outputter);
00457     }
00458 
00459     // save log messages
00460     CBufferedLogOutputter logBuffer(1000);
00461     CLOG->insert(&logBuffer, true);
00462 
00463     // make the task bar receiver.  the user can control this app
00464     // through the task bar.
00465     s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
00466 
00467     // run
00468     int result = startup(argc, argv);
00469 
00470     // done with task bar receiver
00471     delete s_taskBarReceiver;
00472 
00473     // done with log buffer
00474     CLOG->remove(&logBuffer);
00475 
00476     delete ARG->m_serverAddress;
00477     return result;
00478 }
00479 
00480 
00481 //
00482 // command line parsing
00483 //
00484 
00485 #define BYE "\nTry `%s --help' for more information."
00486 
00487 static void             (*bye)(int) = &exit;
00488 
00489 static
00490 void
00491 version()
00492 {
00493     LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s",
00494                             ARG->m_pname,
00495                             kVersion,
00496                             kProtocolMajorVersion,
00497                             kProtocolMinorVersion,
00498                             kCopyright));
00499 }
00500 
00501 static
00502 void
00503 help()
00504 {
00505 #if WINAPI_XWINDOWS
00506 #  define USAGE_DISPLAY_ARG     \
00507 " [--display <display>]"
00508 #  define USAGE_DISPLAY_INFO    \
00509 "      --display <display>  connect to the X server at <display>\n"
00510 #else
00511 #  define USAGE_DISPLAY_ARG
00512 #  define USAGE_DISPLAY_INFO
00513 #endif
00514 
00515     LOG((CLOG_PRINT
00516 "Usage: %s"
00517 " [--daemon|--no-daemon]"
00518 " [--debug <level>]"
00519 USAGE_DISPLAY_ARG
00520 " [--name <screen-name>]"
00521 " [--yscroll <delta>]"
00522 " [--restart|--no-restart]"
00523 " <server-address>"
00524 "\n\n"
00525 "Start the synergy mouse/keyboard sharing server.\n"
00526 "\n"
00527 "  -d, --debug <level>      filter out log messages with priorty below level.\n"
00528 "                           level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
00529 "                           DEBUG, DEBUG1, DEBUG2.\n"
00530 USAGE_DISPLAY_INFO
00531 "  -f, --no-daemon          run the client in the foreground.\n"
00532 "*     --daemon             run the client as a daemon.\n"
00533 "  -n, --name <screen-name> use screen-name instead the hostname to identify\n"
00534 "                           ourself to the server.\n"
00535 "      --yscroll <delta>    defines the vertical scrolling delta, which is\n"
00536 "                           120 by default.\n"
00537 "  -1, --no-restart         do not try to restart the client if it fails for\n"
00538 "                           some reason.\n"
00539 "*     --restart            restart the client automatically if it fails.\n"
00540 "  -h, --help               display this help and exit.\n"
00541 "      --version            display version information and exit.\n"
00542 "\n"
00543 "* marks defaults.\n"
00544 "\n"
00545 "The server address is of the form: [<hostname>][:<port>].  The hostname\n"
00546 "must be the address or hostname of the server.  The port overrides the\n"
00547 "default port, %d.\n"
00548 "\n"
00549 "Where log messages go depends on the platform and whether or not the\n"
00550 "client is running as a daemon.",
00551                                 ARG->m_pname, kDefaultPort));
00552 
00553 }
00554 
00555 static
00556 bool
00557 isArg(int argi, int argc, const char* const* argv,
00558                 const char* name1, const char* name2,
00559                 int minRequiredParameters = 0)
00560 {
00561     if ((name1 != NULL && strcmp(argv[argi], name1) == 0) ||
00562         (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
00563         // match.  check args left.
00564         if (argi + minRequiredParameters >= argc) {
00565             LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
00566                                 ARG->m_pname, argv[argi], ARG->m_pname));
00567             bye(kExitArgs);
00568         }
00569         return true;
00570     }
00571 
00572     // no match
00573     return false;
00574 }
00575 
00576 static
00577 void
00578 parse(int argc, const char* const* argv)
00579 {
00580     assert(ARG->m_pname != NULL);
00581     assert(argv         != NULL);
00582     assert(argc         >= 1);
00583 
00584     // set defaults
00585     ARG->m_name = ARCH->getHostName();
00586 
00587     // parse options
00588     int i;
00589     for (i = 1; i < argc; ++i) {
00590         if (isArg(i, argc, argv, "-d", "--debug", 1)) {
00591             // change logging level
00592             ARG->m_logFilter = argv[++i];
00593         }
00594 
00595         else if (isArg(i, argc, argv, "-n", "--name", 1)) {
00596             // save screen name
00597             ARG->m_name = argv[++i];
00598         }
00599 
00600         else if (isArg(i, argc, argv, NULL, "--camp")) {
00601             // ignore -- included for backwards compatibility
00602         }
00603 
00604         else if (isArg(i, argc, argv, NULL, "--no-camp")) {
00605             // ignore -- included for backwards compatibility
00606         }
00607 
00608         else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
00609             // not a daemon
00610             ARG->m_daemon = false;
00611         }
00612 
00613         else if (isArg(i, argc, argv, NULL, "--daemon")) {
00614             // daemonize
00615             ARG->m_daemon = true;
00616         }
00617 
00618 #if WINAPI_XWINDOWS
00619         else if (isArg(i, argc, argv, "-display", "--display", 1)) {
00620             // use alternative display
00621             ARG->m_display = argv[++i];
00622         }
00623 #endif
00624 
00625         else if (isArg(i, argc, argv, NULL, "--yscroll", 1)) {
00626             // define scroll 
00627             ARG->m_yscroll = atoi(argv[++i]);
00628         }
00629 
00630         else if (isArg(i, argc, argv, "-1", "--no-restart")) {
00631             // don't try to restart
00632             ARG->m_restartable = false;
00633         }
00634 
00635         else if (isArg(i, argc, argv, NULL, "--restart")) {
00636             // try to restart
00637             ARG->m_restartable = true;
00638         }
00639 
00640         else if (isArg(i, argc, argv, "-z", NULL)) {
00641             ARG->m_backend = true;
00642         }
00643 
00644         else if (isArg(i, argc, argv, "-h", "--help")) {
00645             help();
00646             bye(kExitSuccess);
00647         }
00648 
00649         else if (isArg(i, argc, argv, NULL, "--version")) {
00650             version();
00651             bye(kExitSuccess);
00652         }
00653 
00654         else if (isArg(i, argc, argv, "--", NULL)) {
00655             // remaining arguments are not options
00656             ++i;
00657             break;
00658         }
00659 
00660         else if (argv[i][0] == '-') {
00661             LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
00662                                 ARG->m_pname, argv[i], ARG->m_pname));
00663             bye(kExitArgs);
00664         }
00665 
00666         else {
00667             // this and remaining arguments are not options
00668             break;
00669         }
00670     }
00671 
00672     // exactly one non-option argument (server-address)
00673     if (i == argc) {
00674         LOG((CLOG_PRINT "%s: a server address or name is required" BYE,
00675                                 ARG->m_pname, ARG->m_pname));
00676         bye(kExitArgs);
00677     }
00678     if (i + 1 != argc) {
00679         LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
00680                                 ARG->m_pname, argv[i], ARG->m_pname));
00681         bye(kExitArgs);
00682     }
00683 
00684     // save server address
00685     try {
00686         *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort);
00687         ARG->m_serverAddress->resolve();
00688     }
00689     catch (XSocketAddress& e) {
00690         // allow an address that we can't look up if we're restartable.
00691         // we'll try to resolve the address each time we connect to the
00692         // server.  a bad port will never get better.  patch by Brent
00693         // Priddy.
00694         if (!ARG->m_restartable || e.getError() == XSocketAddress::kBadPort) {
00695             LOG((CLOG_PRINT "%s: %s" BYE,
00696                                 ARG->m_pname, e.what(), ARG->m_pname));
00697             bye(kExitFailed);
00698         }
00699     }
00700 
00701     // increase default filter level for daemon.  the user must
00702     // explicitly request another level for a daemon.
00703     if (ARG->m_daemon && ARG->m_logFilter == NULL) {
00704 #if SYSAPI_WIN32
00705         if (CArchMiscWindows::isWindows95Family()) {
00706             // windows 95 has no place for logging so avoid showing
00707             // the log console window.
00708             ARG->m_logFilter = "FATAL";
00709         }
00710         else
00711 #endif
00712         {
00713             ARG->m_logFilter = "NOTE";
00714         }
00715     }
00716 
00717     // set log filter
00718     if (!CLOG->setFilter(ARG->m_logFilter)) {
00719         LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
00720                                 ARG->m_pname, ARG->m_logFilter, ARG->m_pname));
00721         bye(kExitArgs);
00722     }
00723 
00724     // identify system
00725     LOG((CLOG_INFO "%s Client on %s %s", kAppVersion, ARCH->getOSName().c_str(), ARCH->getPlatformName().c_str()));
00726 
00727 #ifdef WIN32
00728 #ifdef _AMD64_
00729     LOG((CLOG_WARN "This is an experimental x64 build of %s. Use it at your own risk.", kApplication));
00730 #endif
00731 #endif
00732 }
00733 
00734 
00735 //
00736 // platform dependent entry points
00737 //
00738 
00739 #if SYSAPI_WIN32
00740 
00741 static bool             s_hasImportantLogMessages = false;
00742 
00743 //
00744 // CMessageBoxOutputter
00745 //
00746 // This class writes severe log messages to a message box
00747 //
00748 
00749 class CMessageBoxOutputter : public ILogOutputter {
00750 public:
00751     CMessageBoxOutputter() { }
00752     virtual ~CMessageBoxOutputter() { }
00753 
00754     // ILogOutputter overrides
00755     virtual void        open(const char*) { }
00756     virtual void        close() { }
00757     virtual void        show(bool) { }
00758     virtual bool        write(ELevel level, const char* message);
00759     virtual const char* getNewline() const { return ""; }
00760 };
00761 
00762 bool
00763 CMessageBoxOutputter::write(ELevel level, const char* message)
00764 {
00765     // note any important messages the user may need to know about
00766     if (level <= CLog::kWARNING) {
00767         s_hasImportantLogMessages = true;
00768     }
00769 
00770     // FATAL and PRINT messages get a dialog box if not running as
00771     // backend.  if we're running as a backend the user will have
00772     // a chance to see the messages when we exit.
00773     if (!ARG->m_backend && level <= CLog::kFATAL) {
00774         MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING);
00775         return false;
00776     }
00777     else {
00778         return true;
00779     }
00780 }
00781 
00782 static
00783 void
00784 byeThrow(int x)
00785 {
00786     CArchMiscWindows::daemonFailed(x);
00787 }
00788 
00789 static
00790 int
00791 daemonNTMainLoop(int argc, const char** argv)
00792 {
00793     parse(argc, argv);
00794     ARG->m_backend = false;
00795     return CArchMiscWindows::runDaemon(mainLoop);
00796 }
00797 
00798 static
00799 int
00800 daemonNTStartup(int, char**)
00801 {
00802     CSystemLogger sysLogger(DAEMON_NAME, false);
00803     bye = &byeThrow;
00804     return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop);
00805 }
00806 
00807 static
00808 int
00809 foregroundStartup(int argc, char** argv)
00810 {
00811     ARCH->showConsole(false);
00812 
00813     // parse command line
00814     parse(argc, argv);
00815 
00816     // never daemonize
00817     return mainLoop();
00818 }
00819 
00820 static
00821 void
00822 showError(HINSTANCE instance, const char* title, UINT id, const char* arg)
00823 {
00824     CString fmt = CMSWindowsUtil::getString(instance, id);
00825     CString msg = CStringUtil::format(fmt.c_str(), arg);
00826     MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING);
00827 }
00828 
00829 int WINAPI
00830 WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
00831 {
00832     try {
00833         CArchMiscWindows::setIcons((HICON)LoadImage(instance,
00834                                     MAKEINTRESOURCE(IDI_SYNERGY),
00835                                     IMAGE_ICON,
00836                                     32, 32, LR_SHARED),
00837                                     (HICON)LoadImage(instance,
00838                                     MAKEINTRESOURCE(IDI_SYNERGY),
00839                                     IMAGE_ICON,
00840                                     16, 16, LR_SHARED));
00841         CArch arch(instance);
00842         CMSWindowsScreen::init(instance);
00843         CLOG;
00844         CThread::getCurrentThread().setPriority(-14);
00845         CArgs args;
00846 
00847         // set title on log window
00848         ARCH->openConsole((CString(kAppVersion) + " " + "Client").c_str());
00849 
00850         // windows NT family starts services using no command line options.
00851         // since i'm not sure how to tell the difference between that and
00852         // a user providing no options we'll assume that if there are no
00853         // arguments and we're on NT then we're being invoked as a service.
00854         // users on NT can use `--daemon' or `--no-daemon' to force us out
00855         // of the service code path.
00856         StartupFunc startup = &standardStartup;
00857         if (!CArchMiscWindows::isWindows95Family()) {
00858             if (__argc <= 1) {
00859                 startup = &daemonNTStartup;
00860             }
00861             else {
00862                 startup = &foregroundStartup;
00863             }
00864         }
00865 
00866         // send PRINT and FATAL output to a message box
00867         int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
00868 
00869         // let user examine any messages if we're running as a backend
00870         // by putting up a dialog box before exiting.
00871         if (args.m_backend && s_hasImportantLogMessages) {
00872             showError(instance, args.m_pname, IDS_FAILED, "");
00873         }
00874 
00875         delete CLOG;
00876         return result;
00877     }
00878     catch (XBase& e) {
00879         showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what());
00880         //throw;
00881     }
00882     catch (XArch& e) {
00883         showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str());
00884     }
00885     catch (...) {
00886         showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, "<unknown>");
00887         //throw;
00888     }
00889     return kExitFailed;
00890 }
00891 
00892 #elif SYSAPI_UNIX
00893 
00894 int
00895 main(int argc, char** argv)
00896 {
00897     CArgs args;
00898     try {
00899         int result;
00900         CArch arch;
00901         CLOG;
00902         CArgs args;
00903         result = run(argc, argv, NULL, &standardStartup);
00904         delete CLOG;
00905         return result;
00906     }
00907     catch (XBase& e) {
00908         LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what()));
00909         throw;
00910     }
00911     catch (XArch& e) {
00912         LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str()));
00913         return kExitFailed;
00914     }
00915     catch (...) {
00916         LOG((CLOG_CRIT "Uncaught exception: <unknown exception>\n"));
00917         throw;
00918     }
00919 }
00920 
00921 #else
00922 
00923 #error no main() for platform
00924 
00925 #endif

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