source: box/trunk/lib/server/Daemon.cpp @ 3059

Revision 3059, 23.9 KB checked in by chris, 4 months ago (diff)

Split option processing out of Daemon::Main() to ease use of Daemon class in tests.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    Daemon.cpp
5//              Purpose: Basic daemon functionality
6//              Created: 2003/07/29
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef HAVE_UNISTD_H
13        #include <unistd.h>
14#endif
15
16#include <errno.h>
17#include <stdio.h>
18#include <signal.h>
19#include <string.h>
20#include <stdarg.h>
21
22#ifdef HAVE_BSD_UNISTD_H
23        #include <bsd/unistd.h>
24#endif
25
26#ifdef WIN32
27        #include <ws2tcpip.h>
28        #include <process.h>
29#endif
30
31#include "depot.h"
32
33#include <iostream>
34
35#ifdef NEED_BOX_VERSION_H
36#       include "BoxVersion.h"
37#endif
38
39#include "Configuration.h"
40#include "Daemon.h"
41#include "FileModificationTime.h"
42#include "Guards.h"
43#include "Logging.h"
44#include "ServerException.h"
45#include "UnixUser.h"
46#include "Utils.h"
47
48#include "MemLeakFindOn.h"
49
50Daemon *Daemon::spDaemon = 0;
51
52
53// --------------------------------------------------------------------------
54//
55// Function
56//              Name:    Daemon::Daemon()
57//              Purpose: Constructor
58//              Created: 2003/07/29
59//
60// --------------------------------------------------------------------------
61Daemon::Daemon()
62        : mReloadConfigWanted(false),
63          mTerminateWanted(false),
64        #ifdef WIN32
65          mSingleProcess(true),
66          mRunInForeground(true),
67          mKeepConsoleOpenAfterFork(true),
68        #else
69          mSingleProcess(false),
70          mRunInForeground(false),
71          mKeepConsoleOpenAfterFork(false),
72        #endif
73          mHaveConfigFile(false),
74          mLogFileLevel(Log::INVALID),
75          mAppName(DaemonName())
76{
77        // In debug builds, switch on assert failure logging to syslog
78        ASSERT_FAILS_TO_SYSLOG_ON
79        // And trace goes to syslog too
80        TRACE_TO_SYSLOG(true)
81}
82
83// --------------------------------------------------------------------------
84//
85// Function
86//              Name:    Daemon::~Daemon()
87//              Purpose: Destructor
88//              Created: 2003/07/29
89//
90// --------------------------------------------------------------------------
91Daemon::~Daemon()
92{
93}
94
95// --------------------------------------------------------------------------
96//
97// Function
98//              Name:    Daemon::GetOptionString()
99//              Purpose: Returns the valid Getopt command-line options.
100//                       This should be overridden by subclasses to add
101//                       their own options, which should override
102//                       ProcessOption, handle their own, and delegate to
103//                       ProcessOption for the standard options.
104//              Created: 2007/09/18
105//
106// --------------------------------------------------------------------------
107std::string Daemon::GetOptionString()
108{
109        return "c:"
110        #ifndef WIN32
111                "DF"
112        #endif
113                "hkKo:O:PqQt:TUvVW:";
114}
115
116void Daemon::Usage()
117{
118        std::cout << 
119        DaemonBanner() << "\n"
120        "(built with QDBM " << dpversion << ")\n"
121        "\n"
122        "Usage: " << mAppName << " [options] [config file]\n"
123        "\n"
124        "Options:\n"
125        "  -c <file>  Use the specified configuration file. If -c is omitted, the last\n"
126        "             argument is the configuration file, or else the default \n"
127        "             [" << GetConfigFileName() << "]\n"
128#ifndef WIN32
129        "  -D         Debugging mode, do not fork, one process only, one client only\n"
130        "  -F         Do not fork into background, but fork to serve multiple clients\n"
131#endif
132        "  -k         Keep console open after fork, keep writing log messages to it\n"
133        "  -K         Stop writing log messages to console while daemon is running\n"
134        "  -o <file>  Log to a file, defaults to maximum verbosity\n"
135        "  -O <level> Set file log verbosity to error/warning/notice/info/trace/everything\n"
136        "  -P         Show process ID (PID) in console output\n"
137        "  -q         Run more quietly, reduce verbosity level by one, can repeat\n"
138        "  -Q         Run at minimum verbosity, log nothing to console and system\n"
139        "  -t <tag>   Tag console output with specified marker\n"
140        "  -T         Timestamp console output\n"
141        "  -U         Timestamp console output with microseconds\n"
142        "  -v         Run more verbosely, increase verbosity level by one, can repeat\n"
143        "  -V         Run at maximum verbosity, log everything to console and sysystem\n"
144        "  -W <level> Set verbosity to error/warning/notice/info/trace/everything\n"
145        ;
146}
147
148// --------------------------------------------------------------------------
149//
150// Function
151//              Name:    Daemon::ProcessOption(int option)
152//              Purpose: Processes the supplied option (equivalent to the
153//                       return code from getopt()). Return zero if the
154//                       option was handled successfully, or nonzero to
155//                       abort the program with that return value.
156//              Created: 2007/09/18
157//
158// --------------------------------------------------------------------------
159int Daemon::ProcessOption(signed int option)
160{
161        switch(option)
162        {
163                case 'c':
164                {
165                        mConfigFileName = optarg;
166                        mHaveConfigFile = true;
167                }
168                break;
169
170#ifndef WIN32
171                case 'D':
172                {
173                        mSingleProcess = true;
174                }
175                break;
176
177                case 'F':
178                {
179                        mRunInForeground = true;
180                }
181                break;
182#endif // !WIN32
183
184                case 'h':
185                {
186                        Usage();
187                        return 2;
188                }
189                break;
190
191                case 'k':
192                {
193                        mKeepConsoleOpenAfterFork = true;
194                }
195                break;
196
197                case 'K':
198                {
199                        mKeepConsoleOpenAfterFork = false;
200                }
201                break;
202
203                case 'o':
204                {
205                        mLogFile = optarg;
206                        mLogFileLevel = Log::EVERYTHING;
207                }
208                break;
209
210                case 'O':
211                {
212                        mLogFileLevel = Logging::GetNamedLevel(optarg);
213                        if (mLogFileLevel == Log::INVALID)
214                        {
215                                BOX_FATAL("Invalid logging level: " << optarg);
216                                return 2;
217                        }
218                }
219                break;
220
221                case 'P':
222                {
223                        Console::SetShowPID(true);
224                }
225                break;
226
227                case 'q':
228                {
229                        if(mLogLevel == Log::NOTHING)
230                        {
231                                BOX_FATAL("Too many '-q': "
232                                        "Cannot reduce logging "
233                                        "level any more");
234                                return 2;
235                        }
236                        mLogLevel--;
237                }
238                break;
239
240                case 'Q':
241                {
242                        mLogLevel = Log::NOTHING;
243                }
244                break;
245
246                case 't':
247                {
248                        Logging::SetProgramName(optarg);
249                        Console::SetShowTag(true);
250                }
251                break;
252
253                case 'T':
254                {
255                        Console::SetShowTime(true);
256                }
257                break;
258
259                case 'U':
260                {
261                        Console::SetShowTime(true);
262                        Console::SetShowTimeMicros(true);
263                }
264                break;
265
266                case 'v':
267                {
268                        if(mLogLevel == Log::EVERYTHING)
269                        {
270                                BOX_FATAL("Too many '-v': "
271                                        "Cannot increase logging "
272                                        "level any more");
273                                return 2;
274                        }
275                        mLogLevel++;
276                }
277                break;
278
279                case 'V':
280                {
281                        mLogLevel = Log::EVERYTHING;
282                }
283                break;
284
285                case 'W':
286                {
287                        mLogLevel = Logging::GetNamedLevel(optarg);
288                        if (mLogLevel == Log::INVALID)
289                        {
290                                BOX_FATAL("Invalid logging level: " << optarg);
291                                return 2;
292                        }
293                }
294                break;
295
296                case '?':
297                {
298                        BOX_FATAL("Unknown option on command line: " 
299                                << "'" << (char)optopt << "'");
300                        return 2;
301                }
302                break;
303
304                default:
305                {
306                        BOX_FATAL("Unknown error in getopt: returned "
307                                << "'" << option << "'");
308                        return 1;
309                }
310        }
311
312        return 0;
313}
314
315// --------------------------------------------------------------------------
316//
317// Function
318//              Name:    Daemon::Main(const char *, int, const char *[])
319//              Purpose: Parses command-line options, and then calls
320//                      Main(std::string& configFile, bool singleProcess)
321//                      to start the daemon.
322//              Created: 2003/07/29
323//
324// --------------------------------------------------------------------------
325int Daemon::Main(const std::string& rDefaultConfigFile, int argc,
326        const char *argv[])
327{
328        // Find filename of config file
329        mConfigFileName = rDefaultConfigFile;
330        mAppName = argv[0];
331
332        int ret = ProcessOptions(argc, argv);
333        if (ret != 0)
334        {
335                return ret;
336        }
337
338        return Main(mConfigFileName);
339}
340
341// --------------------------------------------------------------------------
342//
343// Function
344//              Name:    Daemon::ProcessOptions(int argc, const char *argv[])
345//              Purpose: Parses command-line options. Useful when you have
346//                       a local Daemon object and don't intend to fork()
347//                       or call Main().
348//              Created: 2008/11/04
349//
350// --------------------------------------------------------------------------
351
352int Daemon::ProcessOptions(int argc, const char *argv[])
353{
354        #ifdef BOX_RELEASE_BUILD
355        mLogLevel = Log::NOTICE;
356        #else
357        mLogLevel = Log::INFO;
358        #endif
359
360        if (argc == 2 && strcmp(argv[1], "/?") == 0)
361        {
362                Usage();
363                return 2;
364        }
365
366        signed int c;
367
368        // reset getopt, just in case anybody used it before.
369        // unfortunately glibc and BSD differ on this point!
370        // http://www.ussg.iu.edu/hypermail/linux/kernel/0305.3/0262.html
371        #if HAVE_DECL_OPTRESET == 1 || defined WIN32
372                optind = 1;
373                optreset = 1;
374        #elif defined __GLIBC__
375                optind = 0;
376        #else // Solaris, any others?
377                optind = 1;
378        #endif
379
380        while((c = getopt(argc, (char * const *)argv, 
381                GetOptionString().c_str())) != -1)
382        {
383                int returnCode = ProcessOption(c);
384
385                if (returnCode != 0)
386                {
387                        return returnCode;
388                }
389        }
390
391        if (argc > optind && !mHaveConfigFile)
392        {
393                mConfigFileName = argv[optind]; optind++;
394                mHaveConfigFile = true;
395        }
396
397        if (argc > optind && ::strcmp(argv[optind], "SINGLEPROCESS") == 0)
398        {
399                mSingleProcess = true; optind++;
400        }
401
402        if (argc > optind)
403        {
404                BOX_FATAL("Unknown parameter on command line: "
405                        << "'" << std::string(argv[optind]) << "'");
406                return 2;
407        }
408
409        Logging::FilterConsole((Log::Level)mLogLevel);
410        Logging::FilterSyslog ((Log::Level)mLogLevel);
411
412        if (mLogFileLevel != Log::INVALID)
413        {
414                mapLogFileLogger.reset(
415                        new FileLogger(mLogFile, mLogFileLevel));
416        }
417
418        return 0;
419}
420
421// --------------------------------------------------------------------------
422//
423// Function
424//              Name:    Daemon::Configure(const std::string& rConfigFileName)
425//              Purpose: Loads daemon configuration. Useful when you have
426//                       a local Daemon object and don't intend to fork()
427//                       or call Main().
428//              Created: 2008/04/19
429//
430// --------------------------------------------------------------------------
431
432bool Daemon::Configure(const std::string& rConfigFileName)
433{
434        // Load the configuration file.
435        std::string errors;
436        std::auto_ptr<Configuration> apConfig;
437
438        try
439        {
440                if (!FileExists(rConfigFileName))
441                {
442                        BOX_FATAL("The main configuration file for " <<
443                                DaemonName() << " was not found: " <<
444                                rConfigFileName);
445                        if (!mHaveConfigFile)
446                        {
447                                BOX_WARNING("The default configuration "
448                                        "directory has changed from /etc/box "
449                                        "to /etc/boxbackup");
450                        }
451                        return false;
452                }
453                       
454                apConfig = Configuration::LoadAndVerify(rConfigFileName,
455                        GetConfigVerify(), errors);
456        }
457        catch(BoxException &e)
458        {
459                if(e.GetType() == CommonException::ExceptionType &&
460                        e.GetSubType() == CommonException::OSFileOpenError)
461                {
462                        BOX_ERROR("Failed to open configuration file: "  <<
463                                rConfigFileName);
464                        return false;
465                }
466
467                throw;
468        }
469
470        // Got errors?
471        if(apConfig.get() == 0)
472        {
473                BOX_ERROR("Failed to load or verify configuration file");
474                return false;
475        }
476       
477        if(!Configure(*apConfig))
478        {
479                BOX_ERROR("Failed to verify configuration file");
480                return false;           
481        }
482       
483        // Store configuration
484        mConfigFileName = rConfigFileName;
485        mLoadedConfigModifiedTime = GetConfigFileModifiedTime();
486               
487        return true;
488}
489
490// --------------------------------------------------------------------------
491//
492// Function
493//              Name:    Daemon::Configure(const Configuration& rConfig)
494//              Purpose: Loads daemon configuration. Useful when you have
495//                       a local Daemon object and don't intend to fork()
496//                       or call Main().
497//              Created: 2008/08/12
498//
499// --------------------------------------------------------------------------
500
501bool Daemon::Configure(const Configuration& rConfig)
502{
503        std::string errors;
504
505        // Verify() may modify the configuration, e.g. adding default values
506        // for required keys, so need to make a copy here
507        std::auto_ptr<Configuration> apConf(new Configuration(rConfig));
508        apConf->Verify(*GetConfigVerify(), errors);
509
510        // Got errors?
511        if(!errors.empty())
512        {
513                BOX_ERROR("Configuration errors: " << errors);
514                return false;
515        }
516       
517        // Store configuration
518        mapConfiguration = apConf;
519       
520        // Let the derived class have a go at setting up stuff
521        // in the initial process
522        SetupInInitialProcess();
523               
524        return true;
525}
526
527// --------------------------------------------------------------------------
528//
529// Function
530//              Name:    Daemon::Main(const std::string& rConfigFileName)
531//              Purpose: Starts the daemon off -- equivalent of C main() function
532//              Created: 2003/07/29
533//
534// --------------------------------------------------------------------------
535int Daemon::Main(const std::string &rConfigFileName)
536{
537        // Banner (optional)
538        {
539                BOX_SYSLOG(Log::NOTICE, DaemonBanner());
540        }
541
542        std::string pidFileName;
543
544        bool asDaemon = !mSingleProcess && !mRunInForeground;
545
546        try
547        {
548                if (!Configure(rConfigFileName))
549                {
550                        BOX_FATAL("Failed to start: failed to load "
551                                "configuration file: " << rConfigFileName);
552                        return 1;
553                }
554               
555                // Server configuration
556                const Configuration &serverConfig(
557                        mapConfiguration->GetSubConfiguration("Server"));
558
559                if(serverConfig.KeyExists("LogFacility"))
560                {
561                        std::string facility =
562                                serverConfig.GetKeyValue("LogFacility");
563                        Logging::SetFacility(Syslog::GetNamedFacility(facility));
564                }
565
566                // Open PID file for writing
567                pidFileName = serverConfig.GetKeyValue("PidFile");
568                FileHandleGuard<(O_WRONLY | O_CREAT | O_TRUNC), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)> pidFile(pidFileName.c_str());
569       
570#ifndef WIN32
571                // Handle changing to a different user
572                if(serverConfig.KeyExists("User"))
573                {
574                        // Config file specifies an user -- look up
575                        UnixUser daemonUser(serverConfig.GetKeyValue("User").c_str());
576                       
577                        // Change the owner on the PID file, so it can be deleted properly on termination
578                        if(::fchown(pidFile, daemonUser.GetUID(), daemonUser.GetGID()) != 0)
579                        {
580                                THROW_EXCEPTION(ServerException, CouldNotChangePIDFileOwner)
581                        }
582                       
583                        // Change the process ID
584                        daemonUser.ChangeProcessUser();
585                }
586
587                if(asDaemon)
588                {
589                        // Let's go... Daemonise...
590                        switch(::fork())
591                        {
592                        case -1:
593                                // error
594                                THROW_EXCEPTION(ServerException, DaemoniseFailed)
595                                break;
596
597                        default:
598                                // parent
599                                // _exit(0);
600                                return 0;
601                                break;
602
603                        case 0:
604                                // child
605                                break;
606                        }
607
608                        // In child
609
610                        // Set new session
611                        if(::setsid() == -1)
612                        {
613                                BOX_LOG_SYS_ERROR("Failed to setsid()");
614                                THROW_EXCEPTION(ServerException, DaemoniseFailed)
615                        }
616
617                        // Fork again...
618                        switch(::fork())
619                        {
620                        case -1:
621                                // error
622                                BOX_LOG_SYS_ERROR("Failed to fork() a child");
623                                THROW_EXCEPTION(ServerException, DaemoniseFailed)
624                                break;
625
626                        default:
627                                // parent
628                                _exit(0);
629                                return 0;
630                                break;
631
632                        case 0:
633                                // child
634                                break;
635                        }
636                }
637#endif // !WIN32
638               
639                // Must set spDaemon before installing signal handler,
640                // otherwise the handler will crash if invoked too soon.
641                if(spDaemon != NULL)
642                {
643                        THROW_EXCEPTION(ServerException, AlreadyDaemonConstructed)
644                }
645                spDaemon = this;
646               
647#ifndef WIN32
648                // Set signal handler
649                // Don't do this in the parent, since it might be anything
650                // (e.g. test/bbackupd)
651               
652                struct sigaction sa;
653                sa.sa_handler = SignalHandler;
654                sa.sa_flags = 0;
655                sigemptyset(&sa.sa_mask); // macro
656                if(::sigaction(SIGHUP, &sa, NULL) != 0 ||
657                        ::sigaction(SIGTERM, &sa, NULL) != 0)
658                {
659                        BOX_LOG_SYS_ERROR("Failed to set signal handlers");
660                        THROW_EXCEPTION(ServerException, DaemoniseFailed)
661                }
662#endif // !WIN32
663
664                // Write PID to file
665                char pid[32];
666
667                int pidsize = sprintf(pid, "%d", (int)getpid());
668
669                if(::write(pidFile, pid, pidsize) != pidsize)
670                {
671                        BOX_LOG_SYS_FATAL("Failed to write PID file: " <<
672                                pidFileName);
673                        THROW_EXCEPTION(ServerException, DaemoniseFailed)
674                }
675               
676                // Set up memory leak reporting
677                #ifdef BOX_MEMORY_LEAK_TESTING
678                {
679                        char filename[256];
680                        sprintf(filename, "%s.memleaks", DaemonName());
681                        memleakfinder_setup_exit_report(filename, DaemonName());
682                }
683                #endif // BOX_MEMORY_LEAK_TESTING
684       
685                if(asDaemon && !mKeepConsoleOpenAfterFork)
686                {
687#ifndef WIN32
688                        // Close standard streams
689                        ::close(0);
690                        ::close(1);
691                        ::close(2);
692                       
693                        // Open and redirect them into /dev/null
694                        int devnull = ::open(PLATFORM_DEV_NULL, O_RDWR, 0);
695                        if(devnull == -1)
696                        {
697                                BOX_LOG_SYS_ERROR("Failed to open /dev/null");
698                                THROW_EXCEPTION(CommonException, OSFileError);
699                        }
700                        // Then duplicate them to all three handles
701                        if(devnull != 0) dup2(devnull, 0);
702                        if(devnull != 1) dup2(devnull, 1);
703                        if(devnull != 2) dup2(devnull, 2);
704                        // Close the original handle if it was opened above the std* range
705                        if(devnull > 2)
706                        {
707                                ::close(devnull);
708                        }
709
710                        // And definitely don't try and send anything to those file descriptors
711                        // -- this has in the past sent text to something which isn't expecting it.
712                        TRACE_TO_STDOUT(false);
713#endif // ! WIN32
714                        Logging::ToConsole(false);
715                }
716
717                // Log the start message
718                BOX_NOTICE("Starting daemon, version: " << BOX_VERSION);
719                BOX_NOTICE("Using configuration file: " << mConfigFileName);
720        }
721        catch(BoxException &e)
722        {
723                BOX_FATAL("Failed to start: exception " << e.what() 
724                        << " (" << e.GetType() 
725                        << "/"  << e.GetSubType() << ")");
726                return 1;
727        }
728        catch(std::exception &e)
729        {
730                BOX_FATAL("Failed to start: exception " << e.what());
731                return 1;
732        }
733        catch(...)
734        {
735                BOX_FATAL("Failed to start: unknown error");
736                return 1;
737        }
738
739#ifdef WIN32
740        // Under win32 we must initialise the Winsock library
741        // before using sockets
742
743        WSADATA info;
744
745        if (WSAStartup(0x0101, &info) == SOCKET_ERROR)
746        {
747                // will not run without sockets
748                BOX_FATAL("Failed to initialise Windows Sockets");
749                THROW_EXCEPTION(CommonException, Internal)
750        }
751#endif
752
753        int retcode = 0;
754       
755        // Main Daemon running
756        try
757        {
758                while(!mTerminateWanted)
759                {
760                        Run();
761                       
762                        if(mReloadConfigWanted && !mTerminateWanted)
763                        {
764                                // Need to reload that config file...
765                                BOX_NOTICE("Reloading configuration file: "
766                                        << mConfigFileName);
767                                std::string errors;
768                                std::auto_ptr<Configuration> pconfig(
769                                        Configuration::LoadAndVerify(
770                                                mConfigFileName.c_str(),
771                                                GetConfigVerify(), errors));
772
773                                // Got errors?
774                                if(pconfig.get() == 0 || !errors.empty())
775                                {
776                                        // Tell user about errors
777                                        BOX_FATAL("Error in configuration "
778                                                << "file: " << mConfigFileName
779                                                << ": " << errors);
780                                        // And give up
781                                        retcode = 1;
782                                        break;
783                                }
784                               
785                                // Store configuration
786                                mapConfiguration = pconfig;
787                                mLoadedConfigModifiedTime =
788                                        GetConfigFileModifiedTime();
789                               
790                                // Stop being marked for loading config again
791                                mReloadConfigWanted = false;
792                        }
793                }
794               
795                // Delete the PID file
796                ::unlink(pidFileName.c_str());
797               
798                // Log
799                BOX_NOTICE("Terminating daemon");
800        }
801        catch(BoxException &e)
802        {
803                BOX_FATAL("Terminating due to exception " << e.what() 
804                        << " (" << e.GetType() 
805                        << "/"  << e.GetSubType() << ")");
806                retcode = 1;
807        }
808        catch(std::exception &e)
809        {
810                BOX_FATAL("Terminating due to exception " << e.what());
811                retcode = 1;
812        }
813        catch(...)
814        {
815                BOX_FATAL("Terminating due to unknown exception");
816                retcode = 1;
817        }
818
819#ifdef WIN32
820        WSACleanup();
821#else
822        // Should clean up here, but it breaks memory leak tests.
823        /*
824        if(asDaemon)
825        {
826                // we are running in the child by now, and should not return
827                mapConfiguration.reset();
828                exit(0);
829        }
830        */
831#endif
832
833        ASSERT(spDaemon == this);
834        spDaemon = NULL;
835
836        return retcode;
837}
838
839// --------------------------------------------------------------------------
840//
841// Function
842//              Name:    Daemon::EnterChild()
843//              Purpose: Sets up for a child task of the main server. Call
844//              just after fork().
845//              Created: 2003/07/31
846//
847// --------------------------------------------------------------------------
848void Daemon::EnterChild()
849{
850#ifndef WIN32
851        // Unset signal handlers
852        struct sigaction sa;
853        sa.sa_handler = SIG_DFL;
854        sa.sa_flags = 0;
855        sigemptyset(&sa.sa_mask);                       // macro
856        ::sigaction(SIGHUP, &sa, NULL);
857        ::sigaction(SIGTERM, &sa, NULL);
858#endif
859}
860
861
862// --------------------------------------------------------------------------
863//
864// Function
865//              Name:    Daemon::SignalHandler(int)
866//              Purpose: Signal handler
867//              Created: 2003/07/29
868//
869// --------------------------------------------------------------------------
870void Daemon::SignalHandler(int sigraised)
871{
872#ifndef WIN32
873        if(spDaemon != 0)
874        {
875                switch(sigraised)
876                {
877                case SIGHUP:
878                        spDaemon->mReloadConfigWanted = true;
879                        break;
880                       
881                case SIGTERM:
882                        spDaemon->mTerminateWanted = true;
883                        break;
884               
885                default:
886                        break;
887                }
888        }
889#endif
890}
891
892// --------------------------------------------------------------------------
893//
894// Function
895//              Name:    Daemon::DaemonName()
896//              Purpose: Returns name of the daemon
897//              Created: 2003/07/29
898//
899// --------------------------------------------------------------------------
900const char *Daemon::DaemonName() const
901{
902        return "generic-daemon";
903}
904
905
906// --------------------------------------------------------------------------
907//
908// Function
909//              Name:    Daemon::DaemonBanner()
910//              Purpose: Returns the text banner for this daemon's startup
911//              Created: 1/1/04
912//
913// --------------------------------------------------------------------------
914std::string Daemon::DaemonBanner() const
915{
916        return "Generic daemon using the Box Application Framework";
917}
918
919
920// --------------------------------------------------------------------------
921//
922// Function
923//              Name:    Daemon::Run()
924//              Purpose: Main run function after basic Daemon initialisation
925//              Created: 2003/07/29
926//
927// --------------------------------------------------------------------------
928void Daemon::Run()
929{
930        while(!StopRun())
931        {
932                ::sleep(10);
933        }
934}
935
936
937// --------------------------------------------------------------------------
938//
939// Function
940//              Name:    Daemon::GetConfigVerify()
941//              Purpose: Returns the configuration file verification structure for this daemon
942//              Created: 2003/07/29
943//
944// --------------------------------------------------------------------------
945const ConfigurationVerify *Daemon::GetConfigVerify() const
946{
947        static ConfigurationVerifyKey verifyserverkeys[] = 
948        {
949                DAEMON_VERIFY_SERVER_KEYS
950        };
951
952        static ConfigurationVerify verifyserver[] = 
953        {
954                {
955                        "Server",
956                        0,
957                        verifyserverkeys,
958                        ConfigTest_Exists | ConfigTest_LastEntry,
959                        0
960                }
961        };
962
963        static ConfigurationVerify verify =
964        {
965                "root",
966                verifyserver,
967                0,
968                ConfigTest_Exists | ConfigTest_LastEntry,
969                0
970        };
971
972        return &verify;
973}
974
975
976// --------------------------------------------------------------------------
977//
978// Function
979//              Name:    Daemon::GetConfiguration()
980//              Purpose: Returns the daemon configuration object
981//              Created: 2003/07/29
982//
983// --------------------------------------------------------------------------
984const Configuration &Daemon::GetConfiguration() const
985{
986        if(mapConfiguration.get() == 0)
987        {
988                // Shouldn't get anywhere near this if a configuration file can't be loaded
989                THROW_EXCEPTION(ServerException, Internal)
990        }
991       
992        return *mapConfiguration;
993}
994
995
996// --------------------------------------------------------------------------
997//
998// Function
999//              Name:    Daemon::SetupInInitialProcess()
1000//              Purpose: A chance for the daemon to do something initial
1001//                       setting up in the process which initiates
1002//                       everything, and after the configuration file has
1003//                       been read and verified.
1004//              Created: 2003/08/20
1005//
1006// --------------------------------------------------------------------------
1007void Daemon::SetupInInitialProcess()
1008{
1009        // Base class doesn't do anything.
1010}
1011
1012
1013void Daemon::SetProcessTitle(const char *format, ...)
1014{
1015        // On OpenBSD, setproctitle() sets the process title to imagename: <text> (imagename)
1016        // -- make sure other platforms include the image name somewhere so ps listings give
1017        // useful information.
1018
1019#ifdef HAVE_SETPROCTITLE
1020        // optional arguments
1021        va_list args;
1022        va_start(args, format);
1023
1024        // Make the string
1025        char title[256];
1026        ::vsnprintf(title, sizeof(title), format, args);
1027       
1028        // Set process title
1029        ::setproctitle("%s", title);
1030       
1031#endif // HAVE_SETPROCTITLE
1032}
1033
1034
1035// --------------------------------------------------------------------------
1036//
1037// Function
1038//              Name:    Daemon::GetConfigFileModifiedTime()
1039//              Purpose: Returns the timestamp when the configuration file
1040//                       was last modified
1041//
1042//              Created: 2006/01/29
1043//
1044// --------------------------------------------------------------------------
1045
1046box_time_t Daemon::GetConfigFileModifiedTime() const
1047{
1048        EMU_STRUCT_STAT st;
1049
1050        if(EMU_STAT(GetConfigFileName().c_str(), &st) != 0)
1051        {
1052                if (errno == ENOENT)
1053                {
1054                        return 0;
1055                }
1056                BOX_LOG_SYS_ERROR("Failed to stat configuration file: " <<
1057                        GetConfigFileName());
1058                THROW_EXCEPTION(CommonException, OSFileError)
1059        }
1060       
1061        return FileModificationTime(st);
1062}
1063
1064// --------------------------------------------------------------------------
1065//
1066// Function
1067//              Name:    Daemon::GetLoadedConfigModifiedTime()
1068//              Purpose: Returns the timestamp when the configuration file
1069//                       had been last modified, at the time when it was
1070//                       loaded
1071//
1072//              Created: 2006/01/29
1073//
1074// --------------------------------------------------------------------------
1075
1076box_time_t Daemon::GetLoadedConfigModifiedTime() const
1077{
1078        return mLoadedConfigModifiedTime;
1079}
1080
Note: See TracBrowser for help on using the repository browser.