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

Last change on this file since 2751 was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

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