~ubuntu-branches/ubuntu/gutsy/poco/gutsy

« back to all changes in this revision

Viewing changes to Util/src/ServerApplication.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Krzysztof Burghardt
  • Date: 2007-04-27 18:33:48 UTC
  • Revision ID: james.westby@ubuntu.com-20070427183348-xgnpct0qd6a2ip34
Tags: upstream-1.2.9
ImportĀ upstreamĀ versionĀ 1.2.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// ServerApplication.cpp
 
3
//
 
4
// $Id: //poco/1.2/Util/src/ServerApplication.cpp#2 $
 
5
//
 
6
// Library: Util
 
7
// Package: Application
 
8
// Module:  ServerApplication
 
9
//
 
10
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
 
11
// and Contributors.
 
12
//
 
13
// Permission is hereby granted, free of charge, to any person or organization
 
14
// obtaining a copy of the software and accompanying documentation covered by
 
15
// this license (the "Software") to use, reproduce, display, distribute,
 
16
// execute, and transmit the Software, and to prepare derivative works of the
 
17
// Software, and to permit third-parties to whom the Software is furnished to
 
18
// do so, all subject to the following:
 
19
// 
 
20
// The copyright notices in the Software and this entire statement, including
 
21
// the above license grant, this restriction and the following disclaimer,
 
22
// must be included in all copies of the Software, in whole or in part, and
 
23
// all derivative works of the Software, unless such copies or derivative
 
24
// works are solely in the form of machine-executable object code generated by
 
25
// a source language processor.
 
26
// 
 
27
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
28
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
29
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 
30
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 
31
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 
32
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 
33
// DEALINGS IN THE SOFTWARE.
 
34
//
 
35
 
 
36
 
 
37
#include "Poco/Util/ServerApplication.h"
 
38
#include "Poco/Util/Option.h"
 
39
#include "Poco/Util/OptionSet.h"
 
40
#include "Poco/Exception.h"
 
41
#include "Poco/Process.h"
 
42
#include "Poco/NumberFormatter.h"
 
43
#include "Poco/NamedEvent.h"
 
44
#include "Poco/Logger.h"
 
45
#if defined(POCO_OS_FAMILY_UNIX)
 
46
#include <stdlib.h>
 
47
#include <unistd.h>
 
48
#include <stdio.h>
 
49
#include <signal.h>
 
50
#include <sys/stat.h>
 
51
#elif defined(POCO_OS_FAMILY_WINDOWS)
 
52
#include "Poco/Util/WinService.h"
 
53
#include <windows.h>
 
54
#include <string.h>
 
55
#endif
 
56
#if defined(POCO_WIN32_UTF8)
 
57
#include "Poco/UnicodeConverter.h"
 
58
#endif
 
59
 
 
60
 
 
61
using Poco::NamedEvent;
 
62
using Poco::Process;
 
63
using Poco::NumberFormatter;
 
64
using Poco::Exception;
 
65
using Poco::SystemException;
 
66
 
 
67
 
 
68
namespace Poco {
 
69
namespace Util {
 
70
 
 
71
 
 
72
#if defined(POCO_OS_FAMILY_WINDOWS)
 
73
Poco::Event     ServerApplication::_terminated;
 
74
SERVICE_STATUS        ServerApplication::_serviceStatus; 
 
75
SERVICE_STATUS_HANDLE ServerApplication::_serviceStatusHandle = 0; 
 
76
#endif
 
77
 
 
78
 
 
79
ServerApplication::ServerApplication()
 
80
{
 
81
#if defined(POCO_OS_FAMILY_WINDOWS)
 
82
        _action = SRV_RUN;
 
83
        memset(&_serviceStatus, 0, sizeof(_serviceStatus));
 
84
#endif
 
85
}
 
86
 
 
87
 
 
88
ServerApplication::~ServerApplication()
 
89
{
 
90
}
 
91
 
 
92
 
 
93
bool ServerApplication::isInteractive() const
 
94
{
 
95
        bool runsInBackground = config().getBool("application.runAsDaemon", false) || config().getBool("application.runAsService", false);
 
96
        return !runsInBackground;
 
97
}
 
98
 
 
99
 
 
100
int ServerApplication::run()
 
101
{
 
102
        return Application::run();
 
103
}
 
104
 
 
105
 
 
106
void ServerApplication::terminate()
 
107
{
 
108
        Process::requestTermination(Process::id());
 
109
}
 
110
 
 
111
 
 
112
#if defined(POCO_OS_FAMILY_WINDOWS)
 
113
 
 
114
 
 
115
//
 
116
// Windows specific code
 
117
//
 
118
BOOL ServerApplication::ConsoleCtrlHandler(DWORD ctrlType)
 
119
{
 
120
        switch (ctrlType) 
 
121
        { 
 
122
        case CTRL_C_EVENT: 
 
123
        case CTRL_CLOSE_EVENT: 
 
124
        case CTRL_BREAK_EVENT:
 
125
                terminate();
 
126
                return _terminated.tryWait(10000) ? TRUE : FALSE;
 
127
        default: 
 
128
                return FALSE; 
 
129
        }
 
130
}
 
131
 
 
132
 
 
133
void ServerApplication::ServiceControlHandler(DWORD control)
 
134
{
 
135
        switch (control) 
 
136
        { 
 
137
        case SERVICE_CONTROL_STOP:
 
138
        case SERVICE_CONTROL_SHUTDOWN:
 
139
                terminate();
 
140
                _serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
 
141
                break;
 
142
        case SERVICE_CONTROL_INTERROGATE: 
 
143
                break; 
 
144
        } 
 
145
        SetServiceStatus(_serviceStatusHandle,  &_serviceStatus);
 
146
}
 
147
 
 
148
 
 
149
void ServerApplication::ServiceMain(DWORD argc, LPTSTR* argv)
 
150
{
 
151
        ServerApplication& app = static_cast<ServerApplication&>(Application::instance());
 
152
 
 
153
#if defined(POCO_WIN32_UTF8)
 
154
        _serviceStatusHandle = RegisterServiceCtrlHandlerW(L"", ServiceControlHandler);
 
155
#else
 
156
        _serviceStatusHandle = RegisterServiceCtrlHandler("", ServiceControlHandler);
 
157
#endif
 
158
        if (!_serviceStatusHandle)
 
159
                throw SystemException("cannot register service control handler");
 
160
 
 
161
        _serviceStatus.dwServiceType             = SERVICE_WIN32; 
 
162
        _serviceStatus.dwCurrentState            = SERVICE_START_PENDING; 
 
163
        _serviceStatus.dwControlsAccepted        = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
 
164
        _serviceStatus.dwWin32ExitCode           = 0; 
 
165
        _serviceStatus.dwServiceSpecificExitCode = 0; 
 
166
        _serviceStatus.dwCheckPoint              = 0; 
 
167
        _serviceStatus.dwWaitHint                = 0; 
 
168
        SetServiceStatus(_serviceStatusHandle, &_serviceStatus);
 
169
 
 
170
        try
 
171
        {
 
172
#if defined(POCO_WIN32_UTF8)
 
173
                std::vector<std::string> args;
 
174
                for (DWORD i = 0; i < argc; ++i)
 
175
                {
 
176
                        std::string arg;
 
177
                        Poco::UnicodeConverter::toUTF8(argv[i], arg);
 
178
                        args.push_back(arg);
 
179
                }
 
180
                app.init(args);
 
181
#else
 
182
                app.init(argc, argv);
 
183
#endif
 
184
                _serviceStatus.dwCurrentState = SERVICE_RUNNING; 
 
185
                SetServiceStatus(_serviceStatusHandle, &_serviceStatus);
 
186
                int rc = app.run();
 
187
                _serviceStatus.dwWin32ExitCode           = rc ? ERROR_SERVICE_SPECIFIC_ERROR : 0;
 
188
                _serviceStatus.dwServiceSpecificExitCode = rc; 
 
189
        }
 
190
        catch (Exception& exc)
 
191
        {
 
192
                app.logger().log(exc);
 
193
                _serviceStatus.dwWin32ExitCode           = ERROR_SERVICE_SPECIFIC_ERROR;
 
194
                _serviceStatus.dwServiceSpecificExitCode = EXIT_CONFIG; 
 
195
        }
 
196
        catch (...)
 
197
        {
 
198
                app.logger().error("fatal error - aborting");
 
199
                _serviceStatus.dwWin32ExitCode           = ERROR_SERVICE_SPECIFIC_ERROR;
 
200
                _serviceStatus.dwServiceSpecificExitCode = EXIT_SOFTWARE; 
 
201
        }
 
202
        try
 
203
        {
 
204
                app.uninitialize();
 
205
        }
 
206
        catch (...)
 
207
        {
 
208
        }
 
209
        _serviceStatus.dwCurrentState = SERVICE_STOPPED;
 
210
        SetServiceStatus(_serviceStatusHandle, &_serviceStatus);
 
211
}
 
212
 
 
213
 
 
214
void ServerApplication::waitForTerminationRequest()
 
215
{
 
216
        SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
 
217
        std::string evName("POCOTRM");
 
218
        evName.append(NumberFormatter::formatHex(Process::id(), 8));
 
219
        NamedEvent ev(evName);
 
220
        ev.wait();
 
221
        _terminated.set();
 
222
}
 
223
 
 
224
 
 
225
int ServerApplication::run(int argc, char** argv)
 
226
{
 
227
        if (!hasConsole() && isService())
 
228
        {
 
229
                config().setBool("application.runAsService", true);
 
230
                return 0;
 
231
        }
 
232
        else 
 
233
        {
 
234
                int rc = EXIT_OK;
 
235
                try
 
236
                {
 
237
                        init(argc, argv);
 
238
                        switch (_action)
 
239
                        {
 
240
                        case SRV_REGISTER:
 
241
                                registerService();
 
242
                                rc = EXIT_OK;
 
243
                                break;
 
244
                        case SRV_UNREGISTER:
 
245
                                unregisterService();
 
246
                                rc = EXIT_OK;
 
247
                                break;
 
248
                        default:
 
249
                                rc = run();
 
250
                                uninitialize();
 
251
                        }
 
252
                }
 
253
                catch (Exception& exc)
 
254
                {
 
255
                        logger().log(exc);
 
256
                        rc = EXIT_SOFTWARE;
 
257
                }
 
258
                return rc;
 
259
        }
 
260
}
 
261
 
 
262
 
 
263
#if defined(POCO_WIN32_UTF8)
 
264
int ServerApplication::run(int argc, wchar_t** argv)
 
265
{
 
266
        if (!hasConsole() && isService())
 
267
        {
 
268
                config().setBool("application.runAsService", true);
 
269
                return 0;
 
270
        }
 
271
        else 
 
272
        {
 
273
                int rc = EXIT_OK;
 
274
                try
 
275
                {
 
276
                        init(argc, argv);
 
277
                        switch (_action)
 
278
                        {
 
279
                        case SRV_REGISTER:
 
280
                                registerService();
 
281
                                rc = EXIT_OK;
 
282
                                break;
 
283
                        case SRV_UNREGISTER:
 
284
                                unregisterService();
 
285
                                rc = EXIT_OK;
 
286
                                break;
 
287
                        default:
 
288
                                rc = run();
 
289
                                uninitialize();
 
290
                        }
 
291
                }
 
292
                catch (Exception& exc)
 
293
                {
 
294
                        logger().log(exc);
 
295
                        rc = EXIT_SOFTWARE;
 
296
                }
 
297
                return rc;
 
298
        }
 
299
}
 
300
#endif
 
301
 
 
302
 
 
303
bool ServerApplication::isService()
 
304
{
 
305
        SERVICE_TABLE_ENTRY svcDispatchTable[2];
 
306
#if defined(POCO_WIN32_UTF8)
 
307
        svcDispatchTable[0].lpServiceName = L"";
 
308
#else
 
309
        svcDispatchTable[0].lpServiceName = "";
 
310
#endif
 
311
        svcDispatchTable[0].lpServiceProc = ServiceMain;
 
312
        svcDispatchTable[1].lpServiceName = NULL;
 
313
        svcDispatchTable[1].lpServiceProc = NULL; 
 
314
 
 
315
        return StartServiceCtrlDispatcher(svcDispatchTable) != 0; 
 
316
}
 
317
 
 
318
 
 
319
bool ServerApplication::hasConsole()
 
320
{
 
321
        HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
 
322
        return  hStdOut != INVALID_HANDLE_VALUE && hStdOut != NULL;
 
323
}
 
324
 
 
325
 
 
326
void ServerApplication::registerService()
 
327
{
 
328
        std::string name = config().getString("application.baseName");
 
329
        std::string path = config().getString("application.path");
 
330
        
 
331
        WinService service(name);
 
332
        if (_displayName.empty())
 
333
                service.registerService(path);
 
334
        else
 
335
                service.registerService(path, _displayName);
 
336
        logger().information("The application has been successfully registered as a service");
 
337
}
 
338
 
 
339
 
 
340
void ServerApplication::unregisterService()
 
341
{
 
342
        std::string name = config().getString("application.baseName");
 
343
        
 
344
        WinService service(name);
 
345
        service.unregisterService();
 
346
        logger().information("The service has been successfully unregistered");
 
347
}
 
348
 
 
349
 
 
350
void ServerApplication::defineOptions(OptionSet& options)
 
351
{
 
352
        Application::defineOptions(options);
 
353
 
 
354
        options.addOption(
 
355
                Option("registerService", "", "register application as a service")
 
356
                        .required(false)
 
357
                        .repeatable(false));
 
358
 
 
359
        options.addOption(
 
360
                Option("unregisterService", "", "unregister application as a service")
 
361
                        .required(false)
 
362
                        .repeatable(false));
 
363
 
 
364
        options.addOption(
 
365
                Option("displayName", "", "specify a display name for the service (only with /registerService)")
 
366
                        .required(false)
 
367
                        .repeatable(false)
 
368
                        .argument("name"));
 
369
}
 
370
 
 
371
 
 
372
void ServerApplication::handleOption(const std::string& name, const std::string& value)
 
373
{
 
374
        if (name == "registerService")
 
375
                _action = SRV_REGISTER;
 
376
        else if (name == "unregisterService")
 
377
                _action = SRV_UNREGISTER;
 
378
        else if (name == "displayName")
 
379
                _displayName = value;
 
380
        else
 
381
                Application::handleOption(name, value);
 
382
}
 
383
 
 
384
 
 
385
#elif defined(POCO_OS_FAMILY_UNIX)
 
386
 
 
387
 
 
388
//
 
389
// Unix specific code
 
390
//
 
391
void ServerApplication::waitForTerminationRequest()
 
392
{
 
393
        sigset_t sset;
 
394
        sigemptyset(&sset);
 
395
        sigaddset(&sset, SIGINT);
 
396
        sigaddset(&sset, SIGQUIT);
 
397
        sigaddset(&sset, SIGTERM);
 
398
        sigprocmask(SIG_BLOCK, &sset, NULL);
 
399
        int sig;
 
400
        sigwait(&sset, &sig);
 
401
}
 
402
 
 
403
 
 
404
int ServerApplication::run(int argc, char** argv)
 
405
{
 
406
        bool runAsDaemon = isDaemon(argc, argv);
 
407
        if (runAsDaemon)
 
408
        {
 
409
                beDaemon();
 
410
        }
 
411
        try
 
412
        {
 
413
                init(argc, argv);
 
414
                if (runAsDaemon)
 
415
                {
 
416
                        chdir("/");
 
417
                }
 
418
        }
 
419
        catch (Exception& exc)
 
420
        {
 
421
                logger().log(exc);
 
422
                return EXIT_CONFIG;
 
423
        }
 
424
        int rc = run();
 
425
        try
 
426
        {
 
427
                uninitialize();
 
428
        }
 
429
        catch (Exception& exc)
 
430
        {
 
431
                logger().log(exc);
 
432
                rc = EXIT_CONFIG;
 
433
        }
 
434
        return rc;
 
435
}
 
436
 
 
437
 
 
438
bool ServerApplication::isDaemon(int argc, char** argv)
 
439
{
 
440
        std::string option("--daemon");
 
441
        for (int i = 1; i < argc; ++i)
 
442
        {
 
443
                if (option == argv[i])
 
444
                        return true;
 
445
        }
 
446
        return false;
 
447
}
 
448
 
 
449
 
 
450
void ServerApplication::beDaemon()
 
451
{
 
452
        pid_t pid;
 
453
        if ((pid = fork()) < 0)
 
454
                throw SystemException("cannot fork daemon process");
 
455
        else if (pid != 0)
 
456
                exit(0);
 
457
        
 
458
        setsid();
 
459
        umask(0);
 
460
        close(0);
 
461
        close(1);
 
462
        close(2);
 
463
}
 
464
 
 
465
 
 
466
void ServerApplication::defineOptions(OptionSet& options)
 
467
{
 
468
        Application::defineOptions(options);
 
469
 
 
470
        options.addOption(
 
471
                Option("daemon", "", "run application as a daemon")
 
472
                        .required(false)
 
473
                        .repeatable(false));
 
474
}
 
475
 
 
476
 
 
477
void ServerApplication::handleOption(const std::string& name, const std::string& value)
 
478
{
 
479
        if (name == "daemon")
 
480
        {
 
481
                config().setBool("application.runAsDaemon", true);
 
482
        }
 
483
        else Application::handleOption(name, value);
 
484
}
 
485
 
 
486
 
 
487
#elif defined(POCO_OS_FAMILY_VMS)
 
488
 
 
489
 
 
490
//
 
491
// VMS specific code
 
492
//
 
493
namespace
 
494
{
 
495
        static void handleSignal(int sig)
 
496
        {
 
497
                ServerApplication::terminate();
 
498
        }
 
499
}
 
500
 
 
501
 
 
502
void ServerApplication::waitForTerminationRequest()
 
503
{
 
504
        struct sigaction handler;
 
505
        handler.sa_handler = handleSignal;
 
506
        handler.sa_flags   = 0;
 
507
        sigemptyset(&handler.sa_mask);
 
508
        sigaction(SIGINT, &handler, NULL);
 
509
        sigaction(SIGQUIT, &handler, NULL);                                       
 
510
 
 
511
        long ctrlY = LIB$M_CLI_CTRLY;
 
512
        unsigned short ioChan;
 
513
        $DESCRIPTOR(ttDsc, "TT:");
 
514
 
 
515
        lib$disable_ctrl(&ctrlY);
 
516
        sys$assign(&ttDsc, &ioChan, 0, 0);
 
517
        sys$qiow(0, ioChan, IO$_SETMODE | IO$M_CTRLYAST, 0, 0, 0, terminate, 0, 0, 0, 0, 0);
 
518
        sys$qiow(0, ioChan, IO$_SETMODE | IO$M_CTRLCAST, 0, 0, 0, terminate, 0, 0, 0, 0, 0);
 
519
 
 
520
        std::string evName("POCOTRM");
 
521
        evName.append(NumberFormatter::formatHex(Process::id(), 8));
 
522
        NamedEvent ev(evName);
 
523
        try
 
524
        {
 
525
                ev.wait();
 
526
    }
 
527
        catch (...)
 
528
        {
 
529
                // CTRL-C will cause an exception to be raised
 
530
        }
 
531
        sys$dassgn(ioChan);
 
532
        lib$enable_ctrl(&ctrlY);
 
533
}
 
534
 
 
535
 
 
536
int ServerApplication::run(int argc, char** argv)
 
537
{
 
538
        try
 
539
        {
 
540
                init(argc, argv);
 
541
        }
 
542
        catch (Exception& exc)
 
543
        {
 
544
                logger().log(exc);
 
545
                return EXIT_CONFIG;
 
546
        }
 
547
        int rc = run();
 
548
        try
 
549
        {
 
550
                uninitialize();
 
551
        }
 
552
        catch (Exception& exc)
 
553
        {
 
554
                logger().log(exc);
 
555
                rc = EXIT_CONFIG;
 
556
        }
 
557
        return rc;
 
558
}
 
559
 
 
560
 
 
561
void ServerApplication::defineOptions(OptionSet& options)
 
562
{
 
563
        Application::defineOptions(options);
 
564
}
 
565
 
 
566
 
 
567
void ServerApplication::handleOption(const std::string& name, const std::string& value)
 
568
{
 
569
        Application::handleOption(name, value);
 
570
}
 
571
 
 
572
 
 
573
#endif
 
574
 
 
575
 
 
576
} } // namespace Poco::Util