source: trunk/synergy/cmd/synergyc/synergyc.cpp@ 3232

Last change on this file since 3232 was 2752, checked in by bird, 19 years ago

Two classes (CPMScreen and CPMKeyState) + the hook dll left (and debugging of course).

File size: 20.5 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2002 Chris Schoeneman
4 *
5 * This package is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * found in the file COPYING that should have accompanied this file.
8 *
9 * This package is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "CClient.h"
16#include "CScreen.h"
17#include "ProtocolTypes.h"
18#include "Version.h"
19#include "XScreen.h"
20#include "CNetworkAddress.h"
21#include "CSocketMultiplexer.h"
22#include "CTCPSocketFactory.h"
23#include "XSocket.h"
24#include "CThread.h"
25#include "CEventQueue.h"
26#include "CFunctionEventJob.h"
27#include "CFunctionJob.h"
28#include "CLog.h"
29#include "CString.h"
30#include "CStringUtil.h"
31#include "LogOutputters.h"
32#include "CArch.h"
33#include "XArch.h"
34#include <cstring>
35
36#define DAEMON_RUNNING(running_)
37#if WINAPI_MSWINDOWS
38#include "CArchMiscWindows.h"
39#include "CMSWindowsScreen.h"
40#include "CMSWindowsUtil.h"
41#include "CMSWindowsClientTaskBarReceiver.h"
42#include "resource.h"
43#undef DAEMON_RUNNING
44#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
45#elif WINAPI_XWINDOWS
46#include "CXWindowsScreen.h"
47#include "CXWindowsClientTaskBarReceiver.h"
48#elif WINAPI_CARBON
49#include "COSXScreen.h"
50#include "COSXClientTaskBarReceiver.h"
51#elif WINAPI_PM
52#include "CPMScreen.h"
53#include "CPMClientTaskBarReceiver.h"
54#endif
55
56// platform dependent name of a daemon
57#if SYSAPI_WIN32
58#define DAEMON_NAME "Synergy Client"
59#elif SYSAPI_UNIX
60#define DAEMON_NAME "synergyc"
61#endif
62
63typedef int (*StartupFunc)(int, char**);
64static bool startClient();
65static void parse(int argc, const char* const* argv);
66
67//
68// program arguments
69//
70
71#define ARG CArgs::s_instance
72
73class CArgs {
74public:
75 CArgs() :
76 m_pname(NULL),
77 m_backend(false),
78 m_restartable(true),
79 m_daemon(true),
80 m_logFilter(NULL),
81 m_display(NULL),
82 m_serverAddress(NULL)
83 { s_instance = this; }
84 ~CArgs() { s_instance = NULL; }
85
86public:
87 static CArgs* s_instance;
88 const char* m_pname;
89 bool m_backend;
90 bool m_restartable;
91 bool m_daemon;
92 const char* m_logFilter;
93 const char* m_display;
94 CString m_name;
95 CNetworkAddress* m_serverAddress;
96};
97
98CArgs* CArgs::s_instance = NULL;
99
100
101//
102// platform dependent factories
103//
104
105static
106CScreen*
107createScreen()
108{
109#if WINAPI_MSWINDOWS
110 return new CScreen(new CMSWindowsScreen(false));
111#elif WINAPI_XWINDOWS
112 return new CScreen(new CXWindowsScreen(ARG->m_display, false));
113#elif WINAPI_CARBON
114 return new CScreen(new COSXScreen(false));
115#elif WINAPI_PM
116 return new CScreen(new CPMScreen(false));
117#endif
118}
119
120static
121CClientTaskBarReceiver*
122createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
123{
124#if WINAPI_MSWINDOWS
125 return new CMSWindowsClientTaskBarReceiver(
126 CMSWindowsScreen::getInstance(), logBuffer);
127#elif WINAPI_XWINDOWS
128 return new CXWindowsClientTaskBarReceiver(logBuffer);
129#elif WINAPI_CARBON
130 return new COSXClientTaskBarReceiver(logBuffer);
131#elif WINAPI_PM
132 return new CPMClientTaskBarReceiver(logBuffer);
133#endif
134}
135
136
137//
138// platform independent main
139//
140
141static CClient* s_client = NULL;
142static CScreen* s_clientScreen = NULL;
143static CClientTaskBarReceiver* s_taskBarReceiver = NULL;
144static double s_retryTime = 0.0;
145static bool s_suspened = false;
146
147static
148void
149updateStatus()
150{
151 s_taskBarReceiver->updateStatus(s_client, "");
152}
153
154static
155void
156updateStatus(const CString& msg)
157{
158 s_taskBarReceiver->updateStatus(s_client, msg);
159}
160
161static
162void
163resetRestartTimeout()
164{
165 s_retryTime = 0.0;
166}
167
168static
169double
170nextRestartTimeout()
171{
172 // choose next restart timeout. we start with rapid retries
173 // then slow down.
174 if (s_retryTime < 1.0) {
175 s_retryTime = 1.0;
176 }
177 else if (s_retryTime < 3.0) {
178 s_retryTime = 3.0;
179 }
180 else if (s_retryTime < 5.0) {
181 s_retryTime = 5.0;
182 }
183 else if (s_retryTime < 15.0) {
184 s_retryTime = 15.0;
185 }
186 else if (s_retryTime < 30.0) {
187 s_retryTime = 30.0;
188 }
189 else {
190 s_retryTime = 60.0;
191 }
192 return s_retryTime;
193}
194
195static
196void
197handleScreenError(const CEvent&, void*)
198{
199 LOG((CLOG_CRIT "error on screen"));
200 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
201}
202
203static
204CScreen*
205openClientScreen()
206{
207 CScreen* screen = createScreen();
208 EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
209 screen->getEventTarget(),
210 new CFunctionEventJob(
211 &handleScreenError));
212 return screen;
213}
214
215static
216void
217closeClientScreen(CScreen* screen)
218{
219 if (screen != NULL) {
220 EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
221 screen->getEventTarget());
222 delete screen;
223 }
224}
225
226static
227void
228handleClientRestart(const CEvent&, void* vtimer)
229{
230 // discard old timer
231 CEventQueueTimer* timer = reinterpret_cast<CEventQueueTimer*>(vtimer);
232 EVENTQUEUE->deleteTimer(timer);
233 EVENTQUEUE->removeHandler(CEvent::kTimer, timer);
234
235 // reconnect
236 startClient();
237}
238
239static
240void
241scheduleClientRestart(double retryTime)
242{
243 // install a timer and handler to retry later
244 LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
245 CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
246 EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
247 new CFunctionEventJob(&handleClientRestart, timer));
248}
249
250static
251void
252handleClientConnected(const CEvent&, void*)
253{
254 LOG((CLOG_NOTE "connected to server"));
255 resetRestartTimeout();
256 updateStatus();
257}
258
259static
260void
261handleClientFailed(const CEvent& e, void*)
262{
263 CClient::CFailInfo* info =
264 reinterpret_cast<CClient::CFailInfo*>(e.getData());
265
266 updateStatus(CString("Failed to connect to server: ") + info->m_what);
267 if (!ARG->m_restartable || !info->m_retry) {
268 LOG((CLOG_ERR "failed to connect to server: %s", info->m_what));
269 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
270 }
271 else {
272 LOG((CLOG_WARN "failed to connect to server: %s", info->m_what));
273 if (!s_suspened) {
274 scheduleClientRestart(nextRestartTimeout());
275 }
276 }
277}
278
279static
280void
281handleClientDisconnected(const CEvent&, void*)
282{
283 LOG((CLOG_NOTE "disconnected from server"));
284 if (!ARG->m_restartable) {
285 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
286 }
287 else if (!s_suspened) {
288 s_client->connect();
289 }
290 updateStatus();
291}
292
293static
294CClient*
295openClient(const CString& name, const CNetworkAddress& address, CScreen* screen)
296{
297 CClient* client = new CClient(name, address,
298 new CTCPSocketFactory, NULL, screen);
299 EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(),
300 client->getEventTarget(),
301 new CFunctionEventJob(handleClientConnected));
302 EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(),
303 client->getEventTarget(),
304 new CFunctionEventJob(handleClientFailed));
305 EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(),
306 client->getEventTarget(),
307 new CFunctionEventJob(handleClientDisconnected));
308 return client;
309}
310
311static
312void
313closeClient(CClient* client)
314{
315 if (client == NULL) {
316 return;
317 }
318
319 EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client);
320 EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client);
321 EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client);
322 delete client;
323}
324
325static
326bool
327startClient()
328{
329 double retryTime;
330 CScreen* clientScreen = NULL;
331 try {
332 if (s_clientScreen == NULL) {
333 clientScreen = openClientScreen();
334 s_client = openClient(ARG->m_name,
335 *ARG->m_serverAddress, clientScreen);
336 s_clientScreen = clientScreen;
337 LOG((CLOG_NOTE "started client"));
338 }
339 s_client->connect();
340 updateStatus();
341 return true;
342 }
343 catch (XScreenUnavailable& e) {
344 LOG((CLOG_WARN "cannot open secondary screen: %s", e.what()));
345 closeClientScreen(clientScreen);
346 updateStatus(CString("Cannot open secondary screen: ") + e.what());
347 retryTime = e.getRetryTime();
348 }
349 catch (XScreenOpenFailure& e) {
350 LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what()));
351 closeClientScreen(clientScreen);
352 return false;
353 }
354 catch (XBase& e) {
355 LOG((CLOG_CRIT "failed to start client: %s", e.what()));
356 closeClientScreen(clientScreen);
357 return false;
358 }
359
360 if (ARG->m_restartable) {
361 scheduleClientRestart(retryTime);
362 return true;
363 }
364 else {
365 // don't try again
366 return false;
367 }
368}
369
370static
371void
372stopClient()
373{
374 closeClient(s_client);
375 closeClientScreen(s_clientScreen);
376 s_client = NULL;
377 s_clientScreen = NULL;
378}
379
380static
381int
382mainLoop()
383{
384 // create socket multiplexer. this must happen after daemonization
385 // on unix because threads evaporate across a fork().
386 CSocketMultiplexer multiplexer;
387
388 // create the event queue
389 CEventQueue eventQueue;
390
391 // start the client. if this return false then we've failed and
392 // we shouldn't retry.
393 LOG((CLOG_DEBUG1 "starting client"));
394 if (!startClient()) {
395 return kExitFailed;
396 }
397
398 // run event loop. if startClient() failed we're supposed to retry
399 // later. the timer installed by startClient() will take care of
400 // that.
401 CEvent event;
402 DAEMON_RUNNING(true);
403 EVENTQUEUE->getEvent(event);
404 while (event.getType() != CEvent::kQuit) {
405 EVENTQUEUE->dispatchEvent(event);
406 CEvent::deleteData(event);
407 EVENTQUEUE->getEvent(event);
408 }
409 DAEMON_RUNNING(false);
410
411 // close down
412 LOG((CLOG_DEBUG1 "stopping client"));
413 stopClient();
414 updateStatus();
415 LOG((CLOG_NOTE "stopped client"));
416
417 return kExitSuccess;
418}
419
420static
421int
422daemonMainLoop(int, const char**)
423{
424#if SYSAPI_WIN32
425 CSystemLogger sysLogger(DAEMON_NAME, false);
426#else
427 CSystemLogger sysLogger(DAEMON_NAME, true);
428#endif
429 return mainLoop();
430}
431
432static
433int
434standardStartup(int argc, char** argv)
435{
436 if (!ARG->m_daemon) {
437 ARCH->showConsole(false);
438 }
439
440 // parse command line
441 parse(argc, argv);
442
443 // daemonize if requested
444 if (ARG->m_daemon) {
445 return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop);
446 }
447 else {
448 return mainLoop();
449 }
450}
451
452static
453int
454run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
455{
456 // general initialization
457 ARG->m_serverAddress = new CNetworkAddress;
458 ARG->m_pname = ARCH->getBasename(argv[0]);
459
460 // install caller's output filter
461 if (outputter != NULL) {
462 CLOG->insert(outputter);
463 }
464
465 // save log messages
466 CBufferedLogOutputter logBuffer(1000);
467 CLOG->insert(&logBuffer, true);
468
469 // make the task bar receiver. the user can control this app
470 // through the task bar.
471 s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
472
473 // run
474 int result = startup(argc, argv);
475
476 // done with task bar receiver
477 delete s_taskBarReceiver;
478
479 // done with log buffer
480 CLOG->remove(&logBuffer);
481
482 delete ARG->m_serverAddress;
483 return result;
484}
485
486
487//
488// command line parsing
489//
490
491#define BYE "\nTry `%s --help' for more information."
492
493static void (*bye)(int) = &exit;
494
495static
496void
497version()
498{
499 LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s",
500 ARG->m_pname,
501 kVersion,
502 kProtocolMajorVersion,
503 kProtocolMinorVersion,
504 kCopyright));
505}
506
507static
508void
509help()
510{
511#if WINAPI_XWINDOWS
512# define USAGE_DISPLAY_ARG \
513" [--display <display>]"
514# define USAGE_DISPLAY_INFO \
515" --display <display> connect to the X server at <display>\n"
516#else
517# define USAGE_DISPLAY_ARG
518# define USAGE_DISPLAY_INFO
519#endif
520
521 LOG((CLOG_PRINT
522"Usage: %s"
523" [--daemon|--no-daemon]"
524" [--debug <level>]"
525USAGE_DISPLAY_ARG
526" [--name <screen-name>]"
527" [--restart|--no-restart]"
528" <server-address>"
529"\n\n"
530"Start the synergy mouse/keyboard sharing server.\n"
531"\n"
532" -d, --debug <level> filter out log messages with priorty below level.\n"
533" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
534" DEBUG, DEBUG1, DEBUG2.\n"
535USAGE_DISPLAY_INFO
536" -f, --no-daemon run the client in the foreground.\n"
537"* --daemon run the client as a daemon.\n"
538" -n, --name <screen-name> use screen-name instead the hostname to identify\n"
539" ourself to the server.\n"
540" -1, --no-restart do not try to restart the client if it fails for\n"
541" some reason.\n"
542"* --restart restart the client automatically if it fails.\n"
543" -h, --help display this help and exit.\n"
544" --version display version information and exit.\n"
545"\n"
546"* marks defaults.\n"
547"\n"
548"The server address is of the form: [<hostname>][:<port>]. The hostname\n"
549"must be the address or hostname of the server. The port overrides the\n"
550"default port, %d.\n"
551"\n"
552"Where log messages go depends on the platform and whether or not the\n"
553"client is running as a daemon.",
554 ARG->m_pname, kDefaultPort));
555
556}
557
558static
559bool
560isArg(int argi, int argc, const char* const* argv,
561 const char* name1, const char* name2,
562 int minRequiredParameters = 0)
563{
564 if ((name1 != NULL && strcmp(argv[argi], name1) == 0) ||
565 (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
566 // match. check args left.
567 if (argi + minRequiredParameters >= argc) {
568 LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
569 ARG->m_pname, argv[argi], ARG->m_pname));
570 bye(kExitArgs);
571 }
572 return true;
573 }
574
575 // no match
576 return false;
577}
578
579static
580void
581parse(int argc, const char* const* argv)
582{
583 assert(ARG->m_pname != NULL);
584 assert(argv != NULL);
585 assert(argc >= 1);
586
587 // set defaults
588 ARG->m_name = ARCH->getHostName();
589
590 // parse options
591 int i;
592 for (i = 1; i < argc; ++i) {
593 if (isArg(i, argc, argv, "-d", "--debug", 1)) {
594 // change logging level
595 ARG->m_logFilter = argv[++i];
596 }
597
598 else if (isArg(i, argc, argv, "-n", "--name", 1)) {
599 // save screen name
600 ARG->m_name = argv[++i];
601 }
602
603 else if (isArg(i, argc, argv, NULL, "--camp")) {
604 // ignore -- included for backwards compatibility
605 }
606
607 else if (isArg(i, argc, argv, NULL, "--no-camp")) {
608 // ignore -- included for backwards compatibility
609 }
610
611 else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
612 // not a daemon
613 ARG->m_daemon = false;
614 }
615
616 else if (isArg(i, argc, argv, NULL, "--daemon")) {
617 // daemonize
618 ARG->m_daemon = true;
619 }
620
621#if WINAPI_XWINDOWS
622 else if (isArg(i, argc, argv, "-display", "--display", 1)) {
623 // use alternative display
624 ARG->m_display = argv[++i];
625 }
626#endif
627
628 else if (isArg(i, argc, argv, "-1", "--no-restart")) {
629 // don't try to restart
630 ARG->m_restartable = false;
631 }
632
633 else if (isArg(i, argc, argv, NULL, "--restart")) {
634 // try to restart
635 ARG->m_restartable = true;
636 }
637
638 else if (isArg(i, argc, argv, "-z", NULL)) {
639 ARG->m_backend = true;
640 }
641
642 else if (isArg(i, argc, argv, "-h", "--help")) {
643 help();
644 bye(kExitSuccess);
645 }
646
647 else if (isArg(i, argc, argv, NULL, "--version")) {
648 version();
649 bye(kExitSuccess);
650 }
651
652 else if (isArg(i, argc, argv, "--", NULL)) {
653 // remaining arguments are not options
654 ++i;
655 break;
656 }
657
658 else if (argv[i][0] == '-') {
659 LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
660 ARG->m_pname, argv[i], ARG->m_pname));
661 bye(kExitArgs);
662 }
663
664 else {
665 // this and remaining arguments are not options
666 break;
667 }
668 }
669
670 // exactly one non-option argument (server-address)
671 if (i == argc) {
672 LOG((CLOG_PRINT "%s: a server address or name is required" BYE,
673 ARG->m_pname, ARG->m_pname));
674 bye(kExitArgs);
675 }
676 if (i + 1 != argc) {
677 LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
678 ARG->m_pname, argv[i], ARG->m_pname));
679 bye(kExitArgs);
680 }
681
682 // save server address
683 try {
684 *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort);
685 ARG->m_serverAddress->resolve();
686 }
687 catch (XSocketAddress& e) {
688 // allow an address that we can't look up if we're restartable.
689 // we'll try to resolve the address each time we connect to the
690 // server. a bad port will never get better. patch by Brent
691 // Priddy.
692 if (!ARG->m_restartable || e.getError() == XSocketAddress::kBadPort) {
693 LOG((CLOG_PRINT "%s: %s" BYE,
694 ARG->m_pname, e.what(), ARG->m_pname));
695 bye(kExitFailed);
696 }
697 }
698
699 // increase default filter level for daemon. the user must
700 // explicitly request another level for a daemon.
701 if (ARG->m_daemon && ARG->m_logFilter == NULL) {
702#if SYSAPI_WIN32
703 if (CArchMiscWindows::isWindows95Family()) {
704 // windows 95 has no place for logging so avoid showing
705 // the log console window.
706 ARG->m_logFilter = "FATAL";
707 }
708 else
709#endif
710 {
711 ARG->m_logFilter = "NOTE";
712 }
713 }
714
715 // set log filter
716 if (!CLOG->setFilter(ARG->m_logFilter)) {
717 LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
718 ARG->m_pname, ARG->m_logFilter, ARG->m_pname));
719 bye(kExitArgs);
720 }
721
722 // identify system
723 LOG((CLOG_INFO "Synergy client %s on %s", kVersion, ARCH->getOSName().c_str()));
724}
725
726
727//
728// platform dependent entry points
729//
730
731#if SYSAPI_WIN32
732
733static bool s_hasImportantLogMessages = false;
734
735//
736// CMessageBoxOutputter
737//
738// This class writes severe log messages to a message box
739//
740
741class CMessageBoxOutputter : public ILogOutputter {
742public:
743 CMessageBoxOutputter() { }
744 virtual ~CMessageBoxOutputter() { }
745
746 // ILogOutputter overrides
747 virtual void open(const char*) { }
748 virtual void close() { }
749 virtual void show(bool) { }
750 virtual bool write(ELevel level, const char* message);
751 virtual const char* getNewline() const { return ""; }
752};
753
754bool
755CMessageBoxOutputter::write(ELevel level, const char* message)
756{
757 // note any important messages the user may need to know about
758 if (level <= CLog::kWARNING) {
759 s_hasImportantLogMessages = true;
760 }
761
762 // FATAL and PRINT messages get a dialog box if not running as
763 // backend. if we're running as a backend the user will have
764 // a chance to see the messages when we exit.
765 if (!ARG->m_backend && level <= CLog::kFATAL) {
766 MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING);
767 return false;
768 }
769 else {
770 return true;
771 }
772}
773
774static
775void
776byeThrow(int x)
777{
778 CArchMiscWindows::daemonFailed(x);
779}
780
781static
782int
783daemonNTMainLoop(int argc, const char** argv)
784{
785 parse(argc, argv);
786 ARG->m_backend = false;
787 return CArchMiscWindows::runDaemon(mainLoop);
788}
789
790static
791int
792daemonNTStartup(int, char**)
793{
794 CSystemLogger sysLogger(DAEMON_NAME, false);
795 bye = &byeThrow;
796 return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop);
797}
798
799static
800int
801foregroundStartup(int argc, char** argv)
802{
803 ARCH->showConsole(false);
804
805 // parse command line
806 parse(argc, argv);
807
808 // never daemonize
809 return mainLoop();
810}
811
812static
813void
814showError(HINSTANCE instance, const char* title, UINT id, const char* arg)
815{
816 CString fmt = CMSWindowsUtil::getString(instance, id);
817 CString msg = CStringUtil::format(fmt.c_str(), arg);
818 MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING);
819}
820
821int WINAPI
822WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
823{
824 try {
825 CArchMiscWindows::setIcons((HICON)LoadImage(instance,
826 MAKEINTRESOURCE(IDI_SYNERGY),
827 IMAGE_ICON,
828 32, 32, LR_SHARED),
829 (HICON)LoadImage(instance,
830 MAKEINTRESOURCE(IDI_SYNERGY),
831 IMAGE_ICON,
832 16, 16, LR_SHARED));
833 CArch arch(instance);
834 CMSWindowsScreen::init(instance);
835 CLOG;
836 CThread::getCurrentThread().setPriority(-14);
837 CArgs args;
838
839 // set title on log window
840 ARCH->openConsole((CString(kAppVersion) + " " + "Client").c_str());
841
842 // windows NT family starts services using no command line options.
843 // since i'm not sure how to tell the difference between that and
844 // a user providing no options we'll assume that if there are no
845 // arguments and we're on NT then we're being invoked as a service.
846 // users on NT can use `--daemon' or `--no-daemon' to force us out
847 // of the service code path.
848 StartupFunc startup = &standardStartup;
849 if (!CArchMiscWindows::isWindows95Family()) {
850 if (__argc <= 1) {
851 startup = &daemonNTStartup;
852 }
853 else {
854 startup = &foregroundStartup;
855 }
856 }
857
858 // send PRINT and FATAL output to a message box
859 int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
860
861 // let user examine any messages if we're running as a backend
862 // by putting up a dialog box before exiting.
863 if (args.m_backend && s_hasImportantLogMessages) {
864 showError(instance, args.m_pname, IDS_FAILED, "");
865 }
866
867 delete CLOG;
868 return result;
869 }
870 catch (XBase& e) {
871 showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what());
872 throw;
873 }
874 catch (XArch& e) {
875 showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str());
876 return kExitFailed;
877 }
878 catch (...) {
879 showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, "<unknown>");
880 throw;
881 }
882}
883
884#elif SYSAPI_UNIX
885
886int
887main(int argc, char** argv)
888{
889 CArgs args;
890 try {
891 int result;
892 CArch arch;
893 CLOG;
894 CArgs args;
895 result = run(argc, argv, NULL, &standardStartup);
896 delete CLOG;
897 return result;
898 }
899 catch (XBase& e) {
900 LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what()));
901 throw;
902 }
903 catch (XArch& e) {
904 LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str()));
905 return kExitFailed;
906 }
907 catch (...) {
908 LOG((CLOG_CRIT "Uncaught exception: <unknown exception>\n"));
909 throw;
910 }
911}
912
913#else
914
915#error no main() for platform
916
917#endif
Note: See TracBrowser for help on using the repository browser.