#include "CClient.h" #include "CString.h" #include "CLog.h" #include "CMutex.h" #include "CNetwork.h" #include "CNetworkAddress.h" #include "CThread.h" #include "XThread.h" #include "ProtocolTypes.h" #include "Version.h" #include // // program arguments // static const char* pname = NULL; static bool s_restartable = true; static bool s_daemon = true; static const char* s_logFilter = NULL; static const char* s_serverName = NULL; // // logging thread safety // static CMutex* s_logMutex = NULL; static void logLock(bool lock) { assert(s_logMutex != NULL); if (lock) { s_logMutex->lock(); } else { s_logMutex->unlock(); } } // // main // void realMain(const CString& name, const CString& hostname, UInt16 port) { // initialize threading library CThread::init(); // make logging thread safe CMutex logMutex; s_logMutex = &logMutex; CLog::setLock(&logLock); CClient* client = NULL; try { // initialize network library CNetwork::init(); // run client CNetworkAddress addr(hostname, port); client = new CClient(name); client->run(addr); // clean up delete client; CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; } catch (...) { // clean up delete client; CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; throw; } } // // command line parsing // static void bye() { log((CLOG_PRINT "Try `%s --help' for more information.", pname)); exit(1); } static void version() { log((CLOG_PRINT "%s %d.%d.%d, protocol version %d.%d\n" "%s", pname, kMajorVersion, kMinorVersion, kReleaseVersion, kProtocolMajorVersion, kProtocolMinorVersion, kCopyright)); } static void help() { log((CLOG_PRINT "Usage: %s" " [--debug ]" " [--daemon|--no-daemon]" " [--restart|--no-restart]" " \n" "Start the synergy mouse/keyboard sharing server.\n" "\n" " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" " -f, --no-daemon run the client in the foreground.\n" " --daemon run the client as a daemon.\n" " -1, --no-restart do not try to restart the client if it fails for\n" " some reason.\n" " --restart restart the client automatically if it fails.\n" " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" "By default, the client is a restartable daemon.\n" "\n" "Where log messages go depends on the platform and whether or not the\n" "client is running as a daemon.", pname)); } static bool isArg(int argi, int argc, char** argv, const char* name1, const char* name2, int minRequiredParameters = 0) { if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || (name2 != NULL && strcmp(argv[argi], name2) == 0)) { // match. check args left. if (argi + minRequiredParameters >= argc) { log((CLOG_PRINT "%s: missing arguments for `%s'", pname, argv[argi])); bye(); } return true; } // no match return false; } static void parse(int argc, char** argv) { assert(pname != NULL); assert(argv != NULL); assert(argc >= 1); // parse options int i; for (i = 1; i < argc; ++i) { if (isArg(i, argc, argv, "-d", "--debug", 1)) { // change logging level s_logFilter = argv[++i]; } else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon s_daemon = false; } else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize s_daemon = true; } else if (isArg(i, argc, argv, "-1", "--no-restart")) { // don't try to restart s_restartable = false; } else if (isArg(i, argc, argv, NULL, "--restart")) { // try to restart s_restartable = true; } else if (isArg(i, argc, argv, "-h", "--help")) { help(); exit(1); } else if (isArg(i, argc, argv, NULL, "--version")) { version(); exit(1); } else if (isArg(i, argc, argv, "--", NULL)) { // remaining arguments are not options ++i; break; } else if (argv[i][0] == '-') { log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); bye(); } else { // this and remaining arguments are not options break; } } // exactly one non-option argument: server-address if (i == argc) { log((CLOG_PRINT "%s: a server address or name is required", pname)); bye(); } if (i + 1 != argc) { log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); bye(); } s_serverName = argv[i]; // set log filter if (!CLog::setFilter(s_logFilter)) { log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter)); bye(); } } // // platform dependent entry points // #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h" #include int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { CMSWindowsScreen::init(instance); // get program name pname = strrchr(argv[0], '/'); if (pname == NULL) { pname = argv[0]; } else { ++pname; } const char* pname2 = strrchr(argv[0], '\\'); if (pname2 != NULL && pname2 > pname) { pname = pname2 + 1; } // FIXME -- direct CLog to MessageBox parse(__argc, __argv); // FIXME -- undirect CLog from MessageBox // FIXME -- if daemon then use win32 event log (however that's done), // otherwise do what? want to use console window for debugging but // not otherwise. // FIXME try { realMain("secondary", s_serverName, 50001); return 0; } catch (XBase& e) { log((CLOG_CRIT "failed: %s", e.what())); CString msg = "failed: "; msg += e.what(); MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); return 1; } catch (XThread&) { // terminated return 1; } } #elif defined(CONFIG_PLATFORM_UNIX) #include #include #include #include #include #include #include #include #include static void daemonize() { // fork so shell thinks we're done and so we're not a process // group leader switch (fork()) { case -1: // failed log((CLOG_PRINT "failed to daemonize")); exit(1); case 0: // child break; default: // parent exits exit(0); } // become leader of a new session setsid(); // chdir to root so we don't keep mounted filesystems points busy chdir("/"); // mask off permissions for any but owner umask(077); // close open files. we only expect stdin, stdout, stderr to be open. close(0); close(1); close(2); // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use // of standard I/O safely goes in the bit bucket. open("/dev/null", O_RDWR); dup(0); dup(0); } static void syslogOutputter(int priority, const char* msg) { // convert priority switch (priority) { case CLog::kFATAL: case CLog::kERROR: priority = LOG_ERR; break; case CLog::kWARNING: priority = LOG_WARNING; break; case CLog::kNOTE: priority = LOG_NOTICE; break; case CLog::kINFO: priority = LOG_INFO; break; default: priority = LOG_DEBUG; break; } // log it syslog(priority, "%s", msg); } int main(int argc, char** argv) { // get program name pname = strrchr(argv[0], '/'); if (pname == NULL) { pname = argv[0]; } else { ++pname; } // parse command line parse(argc, argv); // daemonize if requested if (s_daemon) { daemonize(); // send log to syslog openlog("synergy", 0, LOG_DAEMON); CLog::setOutputter(&syslogOutputter); } // run the server. if running as a daemon then run it in a child // process and restart it as necessary. we have to do this in case // the X server restarts because our process cannot recover from // that. for (;;) { // don't fork if not restartable switch (s_restartable ? fork() : 0) { default: { // parent process. wait for child to exit. int status; if (wait(&status) == -1) { // wait failed. this is unexpected so bail. log((CLOG_CRIT "wait() failed")); return 16; } // what happened? if the child exited normally with a // status less than 16 then the child was deliberately // terminated so we also terminate. otherwise, we // loop. if (WIFEXITED(status) && WEXITSTATUS(status) < 16) { return 0; } break; } case -1: // fork() failed. log the error and proceed as a child log((CLOG_WARN "fork() failed; cannot automatically restart on error")); // fall through case 0: // child process try { realMain("secondary", s_serverName, 50001); return 0; } catch (XBase& e) { log((CLOG_CRIT "failed: %s", e.what())); return 16; } catch (XThread&) { // terminated return 1; } } } } #else #error no main() for platform #endif