added command line parsing, restartability, and daemonizing to
client. broke win32 stuff though. also moved version and copyright constants into a new file and renamed protocol version constants.
This commit is contained in:
parent
e409c83ef9
commit
c3649df304
|
@ -214,15 +214,16 @@ void CClient::runSession(void*)
|
|||
|
||||
// check versions
|
||||
log((CLOG_DEBUG1 "got hello version %d.%d", major, minor));
|
||||
if (major < kMajorVersion ||
|
||||
(major == kMajorVersion && minor < kMinorVersion)) {
|
||||
if (major < kProtocolMajorVersion ||
|
||||
(major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) {
|
||||
throw XIncompatibleClient(major, minor);
|
||||
}
|
||||
|
||||
// say hello back
|
||||
log((CLOG_DEBUG1 "say hello version %d.%d", kMajorVersion, kMinorVersion));
|
||||
log((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion));
|
||||
CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s",
|
||||
kMajorVersion, kMinorVersion, &m_name);
|
||||
kProtocolMajorVersion,
|
||||
kProtocolMinorVersion, &m_name);
|
||||
|
||||
// record streams in a more useful place
|
||||
CLock lock(&m_mutex);
|
||||
|
|
|
@ -6,8 +6,21 @@
|
|||
#include "CNetworkAddress.h"
|
||||
#include "CThread.h"
|
||||
#include "XThread.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "Version.h"
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
@ -70,6 +83,160 @@ void realMain(const CString& name,
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
// 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 <level>]"
|
||||
" [--daemon|--no-daemon]"
|
||||
" [--restart|--no-restart]"
|
||||
" <server-address>\n"
|
||||
"Start the synergy mouse/keyboard sharing server.\n"
|
||||
"\n"
|
||||
" -d, --debug <level> 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
|
||||
//
|
||||
|
@ -77,22 +244,41 @@ void realMain(const CString& name,
|
|||
#if defined(CONFIG_PLATFORM_WIN32)
|
||||
|
||||
#include "CMSWindowsScreen.h"
|
||||
#include <string.h>
|
||||
|
||||
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
CMSWindowsScreen::init(instance);
|
||||
|
||||
if (__argc != 2) {
|
||||
CString msg = "hostname required. exiting.";
|
||||
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
|
||||
return 1;
|
||||
// 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", __argv[1], 50001);
|
||||
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);
|
||||
|
@ -104,29 +290,162 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
|
|||
}
|
||||
}
|
||||
|
||||
#else
|
||||
#elif defined(CONFIG_PLATFORM_UNIX)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <hostname>\n", argv[0]);
|
||||
return 1;
|
||||
// get program name
|
||||
pname = strrchr(argv[0], '/');
|
||||
if (pname == NULL) {
|
||||
pname = argv[0];
|
||||
}
|
||||
else {
|
||||
++pname;
|
||||
}
|
||||
|
||||
try {
|
||||
realMain("secondary", argv[1], 50001);
|
||||
return 0;
|
||||
// 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);
|
||||
}
|
||||
catch (XBase& e) {
|
||||
fprintf(stderr, "failed: %s\n", e.what());
|
||||
return 1;
|
||||
}
|
||||
catch (XThread&) {
|
||||
// terminated
|
||||
return 1;
|
||||
|
||||
// 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
|
||||
|
|
|
@ -1059,7 +1059,8 @@ void CServer::handshakeClient(void* vsocket)
|
|||
// say hello
|
||||
log((CLOG_DEBUG1 "saying hello"));
|
||||
CProtocolUtil::writef(output.get(), "Synergy%2i%2i",
|
||||
kMajorVersion, kMinorVersion);
|
||||
kProtocolMajorVersion,
|
||||
kProtocolMinorVersion);
|
||||
output->flush();
|
||||
|
||||
// wait for the reply
|
||||
|
@ -1117,7 +1118,7 @@ void CServer::handshakeClient(void* vsocket)
|
|||
// FIXME -- could print network address if socket had suitable method
|
||||
log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor()));
|
||||
CProtocolUtil::writef(output.get(), kMsgEIncompatible,
|
||||
kMajorVersion, kMinorVersion);
|
||||
kProtocolMajorVersion, kProtocolMinorVersion);
|
||||
}
|
||||
catch (XBadClient&) {
|
||||
// client not behaving
|
||||
|
|
|
@ -56,13 +56,13 @@ IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor,
|
|||
}
|
||||
|
||||
// disallow connection from test versions to release versions
|
||||
if (major == 0 && kMajorVersion != 0) {
|
||||
if (major == 0 && kProtocolMajorVersion != 0) {
|
||||
throw XIncompatibleClient(major, minor);
|
||||
}
|
||||
|
||||
// hangup (with error) if version isn't supported
|
||||
if (major > kMajorVersion ||
|
||||
(major == kMajorVersion && minor > kMinorVersion)) {
|
||||
if (major > kProtocolMajorVersion ||
|
||||
(major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) {
|
||||
throw XIncompatibleClient(major, minor);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,10 @@
|
|||
#include "CThread.h"
|
||||
#include "XThread.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "Version.h"
|
||||
#include "stdfstream.h"
|
||||
#include <assert.h>
|
||||
|
||||
static const char* s_copyright = "Copyright (C) 2002 Chris Schoeneman";
|
||||
static const SInt32 s_majorVersion = 0;
|
||||
static const SInt32 s_minorVersion = 5;
|
||||
static const char s_releaseVersion = ' ';
|
||||
|
||||
// configuration file name
|
||||
#if defined(CONFIG_PLATFORM_WIN32)
|
||||
#define CONFIG_NAME "synergy.sgc"
|
||||
|
@ -115,15 +111,15 @@ static void bye()
|
|||
static void version()
|
||||
{
|
||||
log((CLOG_PRINT
|
||||
"%s %d.%d%c protocol version %d.%d\n"
|
||||
"%s %d.%d.%d, protocol version %d.%d\n"
|
||||
"%s",
|
||||
pname,
|
||||
s_majorVersion,
|
||||
s_minorVersion,
|
||||
s_releaseVersion,
|
||||
kMajorVersion,
|
||||
kMinorVersion,
|
||||
s_copyright));
|
||||
kReleaseVersion,
|
||||
kProtocolMajorVersion,
|
||||
kProtocolMinorVersion,
|
||||
kCopyright));
|
||||
}
|
||||
|
||||
static void help()
|
||||
|
@ -335,6 +331,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
|
|||
|
||||
// load the configuration file if we haven't already
|
||||
if (s_configFile == NULL) {
|
||||
// FIXME
|
||||
}
|
||||
|
||||
// FIXME
|
||||
|
@ -531,7 +528,6 @@ int main(int argc, char** argv)
|
|||
}
|
||||
catch (XBase& e) {
|
||||
log((CLOG_CRIT "failed: %s", e.what()));
|
||||
fprintf(stderr, "failed: %s\n", e.what());
|
||||
return 16;
|
||||
}
|
||||
catch (XThread&) {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "BasicTypes.h"
|
||||
|
||||
// version number
|
||||
static const SInt32 kMajorVersion = 0;
|
||||
static const SInt32 kMinorVersion = 1;
|
||||
static const SInt16 kProtocolMajorVersion = 0;
|
||||
static const SInt16 kProtocolMinorVersion = 1;
|
||||
|
||||
//
|
||||
// message codes (trailing NUL is not part of code). in comments, $n
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
|
||||
static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman";
|
||||
|
||||
// build version
|
||||
static const SInt16 kMajorVersion = 0;
|
||||
static const SInt16 kMinorVersion = 5;
|
||||
static const SInt16 kReleaseVersion = 0;
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue