win32 changes. changed names of binaries. added support for

running as (and installing/installing) a service.  added
support for multiple desktops (NT only, 95 doesn't support
multiple desktops).
This commit is contained in:
crs
2002-06-08 21:48:00 +00:00
parent 5709d8ddef
commit 4b28ffc5b2
36 changed files with 2948 additions and 855 deletions

View File

@@ -1,6 +1,7 @@
#include "CServer.h"
#include "CConfig.h"
#include "CLog.h"
#include "CLock.h"
#include "CMutex.h"
#include "CNetwork.h"
#include "CPlatform.h"
@@ -11,15 +12,20 @@
#include "stdfstream.h"
#include <assert.h>
// platform dependent name of a daemon
#if defined(CONFIG_PLATFORM_WIN32)
#define DAEMON "service"
#define DAEMON_NAME "Synergy Server"
#elif defined(CONFIG_PLATFORM_UNIX)
#define DAEMON "daemon"
#define DAEMON_NAME "synergyd"
#endif
// configuration file name
#if defined(CONFIG_PLATFORM_WIN32)
#define CONFIG_NAME "synergy.sgc"
#define CONFIG_USER_DIR "%HOME%/"
#define CONFIG_SYS_DIR ""
#elif defined(CONFIG_PLATFORM_UNIX)
#define CONFIG_NAME "synergy.conf"
#define CONFIG_USER_DIR "~/"
#define CONFIG_SYS_DIR "/etc/"
#endif
//
@@ -29,6 +35,8 @@
static const char* pname = NULL;
static bool s_restartable = true;
static bool s_daemon = true;
static bool s_install = false;
static bool s_uninstall = false;
static const char* s_configFile = NULL;
static const char* s_logFilter = NULL;
static CConfig s_config;
@@ -54,47 +62,97 @@ static void logLock(bool lock)
//
// main
// platform independent main
//
void realMain()
static CServer* s_server = NULL;
static int realMain(CMutex* mutex)
{
// initialize threading library
CThread::init();
// s_serverLock should have mutex locked on entry
// make logging thread safe
CMutex logMutex;
s_logMutex = &logMutex;
CLog::setLock(&logLock);
CServer* server = NULL;
try {
// initialize network library
CNetwork::init();
// initialize threading library
CThread::init();
// if configuration has no screens then add this system
// as the default
if (s_config.begin() == s_config.end()) {
s_config.addScreen("primary");
// make logging thread safe
CMutex logMutex;
s_logMutex = &logMutex;
CLog::setLock(&logLock);
bool locked = true;
try {
// initialize network library
CNetwork::init();
// if configuration has no screens then add this system
// as the default
if (s_config.begin() == s_config.end()) {
s_config.addScreen("primary");
}
// create server
s_server = new CServer();
// run server (unlocked)
if (mutex != NULL) {
mutex->unlock();
}
locked = false;
s_server->setConfig(s_config);
s_server->run();
locked = true;
if (mutex != NULL) {
mutex->lock();
}
// clean up
delete s_server;
s_server = NULL;
CNetwork::cleanup();
CLog::setLock(NULL);
s_logMutex = NULL;
}
catch (...) {
// clean up
if (!locked && mutex != NULL) {
mutex->lock();
}
delete s_server;
s_server = NULL;
CNetwork::cleanup();
CLog::setLock(NULL);
s_logMutex = NULL;
throw;
}
// run server
server = new CServer();
server->setConfig(s_config);
server->run();
// clean up
delete server;
CNetwork::cleanup();
CLog::setLock(NULL);
s_logMutex = NULL;
}
catch (...) {
delete server;
CNetwork::cleanup();
CLog::setLock(NULL);
s_logMutex = NULL;
throw;
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
return 16;
}
catch (XThread&) {
// terminated
return 1;
}
return 0;
}
static int restartMain()
{
return realMain(NULL);
}
// invoke realMain and wait for it. if s_restartable then keep
// restarting realMain until it returns a terminate code.
static int restartableMain()
{
if (s_restartable) {
CPlatform platform;
return platform.restart(restartMain, 16);
}
else {
return realMain(NULL);
}
}
@@ -103,11 +161,9 @@ void realMain()
// command line parsing
//
static void bye()
{
log((CLOG_PRINT "Try `%s --help' for more information.", pname));
exit(1);
}
#define BYE "\nTry `%s --help' for more information."
static void (*bye)(int) = &exit;
static void version()
{
@@ -125,12 +181,18 @@ static void version()
static void help()
{
CPlatform platform;
log((CLOG_PRINT
"Usage: %s"
" [--config <pathname>]"
" [--debug <level>]"
" [--daemon|--no-daemon]"
" [--"DAEMON"|--no-"DAEMON"]"
" [--restart|--no-restart]\n"
"or\n"
" --install\n"
" --uninstall\n"
"\n"
"Start the synergy mouse/keyboard sharing server.\n"
"\n"
" -c, --config <pathname> use the named configuration file instead\n"
@@ -138,58 +200,38 @@ static void help()
" -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 server in the foreground.\n"
" --daemon run the server as a daemon.\n"
" -f, --no-"DAEMON" run the server in the foreground.\n"
"* --"DAEMON" run the server as a "DAEMON".\n"
" -1, --no-restart do not try to restart the server if it fails for\n"
" some reason.\n"
" --restart restart the server automatically if it fails.\n"
"* --restart restart the server automatically if it fails.\n"
" --install install server as a "DAEMON".\n"
" --uninstall uninstall server "DAEMON".\n"
" -h, --help display this help and exit.\n"
" --version display version information and exit.\n"
"\n"
"By default, the server is a restartable daemon. If no configuration file\n"
"pathname is provided then the first of the following to load sets the\n"
"configuration:\n"
" " CONFIG_USER_DIR CONFIG_NAME "\n"
" " CONFIG_SYS_DIR CONFIG_NAME "\n"
"* marks defaults.\n"
"\n"
"If no configuration file pathname is provided then the first of the\n"
"following to load sets the configuration:\n"
" %s\n"
" %s\n"
"If no configuration file can be loaded then the configuration uses its\n"
"defaults with just the server screen.\n"
"\n"
"Where log messages go depends on the platform and whether or not the\n"
"server is running as a daemon.",
pname));
}
static bool loadConfig(const char* pathname, bool require)
{
assert(pathname != NULL);
try {
// load configuration
log((CLOG_DEBUG "opening configuration \"%s\"", pathname));
std::ifstream configStream(pathname);
if (!configStream) {
throw XConfigRead("cannot open configuration");
}
configStream >> s_config;
log((CLOG_DEBUG "configuration read successfully"));
return true;
}
catch (XConfigRead& e) {
if (require) {
log((CLOG_PRINT "%s: cannot read configuration '%s'",
pname, pathname));
exit(1);
}
else {
log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname));
}
}
return false;
"server is running as a "DAEMON".",
pname,
platform.addPathComponent(
platform.getUserDirectory(),
CONFIG_NAME).c_str(),
platform.addPathComponent(
platform.getSystemDirectory(),
CONFIG_NAME).c_str()));
}
static bool isArg(int argi,
int argc, char** argv,
int argc, const char** argv,
const char* name1,
const char* name2,
int minRequiredParameters = 0)
@@ -198,9 +240,9 @@ static bool isArg(int argi,
(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();
log((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
pname, argv[argi], pname));
bye(2);
}
return true;
}
@@ -209,7 +251,7 @@ static bool isArg(int argi,
return false;
}
static void parse(int argc, char** argv)
static void parse(int argc, const char** argv)
{
assert(pname != NULL);
assert(argv != NULL);
@@ -228,12 +270,12 @@ static void parse(int argc, char** argv)
s_configFile = argv[++i];
}
else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) {
// not a daemon
s_daemon = false;
}
else if (isArg(i, argc, argv, NULL, "--daemon")) {
else if (isArg(i, argc, argv, NULL, "--"DAEMON)) {
// daemonize
s_daemon = true;
}
@@ -250,12 +292,42 @@ static void parse(int argc, char** argv)
else if (isArg(i, argc, argv, "-h", "--help")) {
help();
exit(1);
bye(0);
}
else if (isArg(i, argc, argv, NULL, "--version")) {
version();
exit(1);
bye(0);
}
else if (isArg(i, argc, argv, NULL, "--install")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_install = true;
if (s_uninstall) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'"
" are mutually exclusive" BYE,
pname, argv[i], pname));
bye(2);
}
}
else if (isArg(i, argc, argv, NULL, "--uninstall")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_uninstall = true;
if (s_install) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'"
" are mutually exclusive" BYE,
pname, argv[i], pname));
bye(2);
}
}
else if (isArg(i, argc, argv, "--", NULL)) {
@@ -265,8 +337,9 @@ static void parse(int argc, char** argv)
}
else if (argv[i][0] == '-') {
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i]));
bye();
log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
pname, argv[i], pname));
bye(2);
}
else {
@@ -277,101 +350,76 @@ static void parse(int argc, char** argv)
// no non-option arguments are allowed
if (i != argc) {
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i]));
bye();
log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
pname, argv[i], pname));
bye(2);
}
// increase default filter level for daemon. the user must
// explicitly request another level for a daemon.
if (s_daemon && s_logFilter == NULL) {
#if defined(CONFIG_PLATFORM_WIN32)
if (CPlatform::isWindows95Family()) {
// windows 95 has no place for logging so avoid showing
// the log console window.
s_logFilter = "FATAL";
}
else
#endif
{
s_logFilter = "NOTE";
}
}
// set log filter
if (!CLog::setFilter(s_logFilter)) {
log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter));
bye();
log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
pname, s_logFilter, pname));
bye(2);
}
}
// load the config file, if any
static bool loadConfig(const char* pathname, bool require)
{
assert(pathname != NULL);
try {
// load configuration
log((CLOG_DEBUG "opening configuration \"%s\"", pathname));
std::ifstream configStream(pathname);
if (!configStream) {
throw XConfigRead("cannot open configuration");
}
configStream >> s_config;
log((CLOG_DEBUG "configuration read successfully"));
return true;
}
catch (XConfigRead&) {
if (require) {
log((CLOG_PRINT "%s: cannot read configuration '%s'",
pname, pathname));
bye(3);
}
else {
log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname));
}
}
return false;
}
static void loadConfig()
{
// load the config file, if specified
if (s_configFile != NULL) {
// require the user specified file to load correctly
loadConfig(s_configFile, true);
}
}
//
// platform dependent entry points
//
#if defined(CONFIG_PLATFORM_WIN32)
#include "CMSWindowsScreen.h"
#include <string.h>
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{
CPlatform platform;
// save instance
CMSWindowsScreen::init(instance);
// get program name
pname = platform.getBasename(argv[0]);
// 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.
// load the configuration file if we haven't already
if (s_configFile == NULL) {
// FIXME
}
// FIXME
if (__argc != 1) {
CString msg = "no arguments allowed. exiting.";
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
return 1;
}
try {
realMain();
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 <unistd.h> // fork()
#include <sys/types.h> // wait()
#include <sys/wait.h> // wait()
int main(int argc, char** argv)
{
CPlatform platform;
// get program name
pname = platform.getBasename(argv[0]);
// parse command line
parse(argc, argv);
// load the configuration file if we haven't already
if (s_configFile == NULL) {
// load the default configuration if no explicit file given
else {
// get the user's home directory. use the effective user id
// so a user can't get a setuid root program to load his file.
CPlatform platform;
bool loaded = false;
CString path = platform.getUserDirectory();
if (!path.empty()) {
@@ -390,62 +438,236 @@ int main(int argc, char** argv)
}
}
}
}
//
// platform dependent entry points
//
#if defined(CONFIG_PLATFORM_WIN32)
#include "CMSWindowsScreen.h"
static bool logMessageBox(int priority, const char* msg)
{
if (priority <= CLog::kFATAL) {
MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING);
return true;
}
else {
return false;
}
}
static void byeThrow(int x)
{
throw CWin32Platform::CDaemonFailed(x);
}
static void daemonStop(void)
{
s_server->quit();
}
static int daemonStartup(IPlatform* iplatform,
int argc, const char** argv)
{
// get platform pointer
CWin32Platform* platform = static_cast<CWin32Platform*>(iplatform);
// catch errors that would normally exit
bye = &byeThrow;
// parse command line
s_install = false;
s_uninstall = false;
parse(argc, argv);
if (s_install || s_uninstall) {
// not allowed to install/uninstall from service
throw CWin32Platform::CDaemonFailed(1);
}
// load configuration
loadConfig();
// run as a service
return platform->runDaemon(realMain, daemonStop);
}
static int daemonStartup95(IPlatform*, int, const char**)
{
return realMain(NULL);
}
static bool logDiscard(int, const char*)
{
return true;
}
static bool s_die = false;
static void checkParse(int e)
{
// anything over 1 means invalid args. 1 means missing args.
// 0 means graceful exit. we plan to exit for anything but
// 1 (missing args); the service control manager may supply
// the missing arguments so we don't exit in that case.
s_die = (e != 1);
throw s_die;
}
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{
CPlatform platform;
// save instance
CMSWindowsScreen::init(instance);
// get program name
pname = platform.getBasename(__argv[0]);
// parse command line without reporting errors but recording if
// the app would've exited. this is too avoid showing a dialog
// box if we're being started as a service because we shouldn't
// take to long to startup as a service. this mostly works but
// will choke if the service control manager passes --install
// or --uninstall (but that's unlikely).
CLog::setOutputter(&logDiscard);
bye = &checkParse;
try {
parse(__argc, const_cast<const char**>(__argv));
}
catch (...) {
// ignore
}
// if we're not starting as an NT service then reparse the command
// line normally.
if (s_die || !s_daemon || s_install || s_uninstall ||
CWin32Platform::isWindows95Family()) {
// send PRINT and FATAL output to a message box
CLog::setOutputter(&logMessageBox);
// exit on bye
bye = &exit;
// reparse
parse(__argc, const_cast<const char**>(__argv));
}
// if starting as a daemon then we ignore the startup command line
// here. we'll parse the command line passed in when the service
// control manager calls us back.
else {
// do nothing
}
// install/uninstall
if (s_install) {
// get the full path to this program
TCHAR path[MAX_PATH];
if (GetModuleFileName(NULL, path,
sizeof(path) / sizeof(path[0])) == 0) {
log((CLOG_CRIT "cannot determine absolute path to program"));
return 16;
}
// construct the command line to start the service with
CString commandLine;
commandLine += "--"DAEMON;
if (s_restartable) {
commandLine += " --restart";
}
else {
commandLine += " --no-restart";
}
if (s_logFilter != NULL) {
commandLine += " --debug ";
commandLine += s_logFilter;
}
if (s_configFile != NULL) {
commandLine += " --config \"";
commandLine += s_configFile;
commandLine += "\"";
}
// install
if (!platform.installDaemon(DAEMON_NAME,
"Shares this system's mouse and keyboard with others.",
path, commandLine.c_str())) {
log((CLOG_CRIT "failed to install service"));
return 16;
}
log((CLOG_PRINT "installed successfully"));
return 0;
}
else if (s_uninstall) {
if (!platform.uninstallDaemon(DAEMON_NAME)) {
log((CLOG_CRIT "failed to uninstall service"));
return 16;
}
log((CLOG_PRINT "uninstalled successfully"));
return 0;
}
// load configuration
loadConfig();
// daemonize if requested
int result;
if (s_daemon) {
if (!platform.daemonize("synergyd")) {
if (CWin32Platform::isWindows95Family()) {
result = platform.daemonize(DAEMON_NAME, &daemonStartup95);
}
else {
result = platform.daemonize(DAEMON_NAME, &daemonStartup);
}
if (result == -1) {
log((CLOG_CRIT "failed to start as a service"));
return 16;
}
}
else {
result = restartableMain();
}
return result;
}
#elif defined(CONFIG_PLATFORM_UNIX)
static int daemonStartup(IPlatform*, int, const char**)
{
return restartableMain();
}
int main(int argc, char** argv)
{
CPlatform platform;
// get program name
pname = platform.getBasename(argv[0]);
// parse command line
parse(argc, const_cast<const char**>(argv));
// load configuration
loadConfig();
// daemonize if requested
int result;
if (s_daemon) {
result = platform.daemonize(DAEMON_NAME, &daemonStartup);
if (result == -1) {
log((CLOG_CRIT "failed to daemonize"));
return 16;
}
}
// 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();
return 0;
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
return 16;
}
catch (XThread&) {
// terminated
return 1;
}
}
else {
result = restartableMain();
}
return result;
}
#else