Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Frameworks/Service/Main.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_Service_Main_h_
5#define _Stroika_Frameworks_Service_Main_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <filesystem>
10#include <optional>
11
13#include "Stroika/Foundation/Common/Common.h"
14#include "Stroika/Foundation/Containers/Sequence.h"
15#include "Stroika/Foundation/Containers/Set.h"
16#include "Stroika/Foundation/Execution/CommandLine.h"
18#include "Stroika/Foundation/Execution/Process.h"
23
24#if qStroika_Foundation_Common_Platform_POSIX
25#include "Stroika/Foundation/Execution/SignalHandlers.h"
26#include "Stroika/Foundation/Execution/Signals.h"
27#endif
28
29/**
30 *
31 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
32 *
33 * TODO:
34 *
35 * @todo Do a better job handling default/reasonable timeout time for service to start/stop/restart.
36 * Hacked something mostly for UNIX.
37 *
38 * @todo Window service not fully working - see why when you start/stop we don't see
39 * Logger::Get ().Log (Logger::eNotice, L"User-service code is shut down");
40 * message printed out. Basic startup/shutdown seems roughly OK though.
41 *
42 * @todo Fix Main::BasicUNIXServiceImpl::SignalHandler_() use of static variable. Race conditions
43 * accessing static var. Use instance var and proper locking.
44 *
45 * @todo Fix Main::BasicUNIXServiceImpl::SignalHandler_ on kSIG_ReReadConfiguration to somehow q message
46 * to re-read, and not invoke re-read from thread (avoid possible deadlock with allocating mem in signal handler).
47 *
48 * @todo Fix majorly hacked (and unsafe anyhow cuz of malloc in signal handler)
49 * stuff. Righjt now needed to make Sercice STOP work under posix, but its
50 * NOT reliably done!!!
51 *
52 * SEE "ADD SAFE SIGNAL HANDLER (DEADLOCK FREE) SUPPORT" in Execution::Signal module
53 *
54 * @todo Carefully review Windows service code for thread locking and races. Complete missing cases, and
55 * bullet proof.
56 *
57 * @todo Support passing extra args to subprocess. For example, if we do --start --logfile=xxx,
58 * We need the invocation of EXE --Run-As-Service to ALSO take --logfile=xxx param. NYI
59 *
60 * @todo Add "Starting" and "Stopping" status values to possible states.
61 * Probably add extra states, and then callback parameter to IApplicationRep::MainLoop()
62 * so it can callback and say status moved from starting to running. Easier to automatically
63 * do the stopping/stopped change (cuz we have the hooks).
64 *
65 * Added the startedCB method - just so fewer incompatible API-breaking changes needed in the
66 * future.
67 *
68 * @todo Implement/test (it maybe implemented) --ReloadConfiguration operation.
69 *
70 * @todo - Main::CommandNames - consider making these STRING params, and then this could be end-user configurable (good idea!)
71 *
72 * @todo Main::CommandNames - provide for command aliases.
73 *
74 * @todo Document clearly the distinction between running service and driver
75 * to control stop/start etc. Think through relationship between windows
76 * approahc and unix approach
77 *
78 * @todo Windoze implementation - supporting the richer set of control mechanism.
79 * AT LEAST support --stop from command-line (currently broken - but you can use net stop or net start servicename;
80 *
81 * @todo Support Pause/Continue
82 *
83 * Long-Term TODO:
84 * @todo Consider adding a 'heartbeat' mechanism - so callback in IApplicationRep
85 * called periodically to see if all OK, and otherwise have recovery strategies.
86 */
87
89
90 using namespace Stroika::Foundation;
91
94 using Execution::pid_t;
95
96#if qStroika_Foundation_Common_Platform_POSIX
97 using Execution::SignalID;
98#endif
99
100 /**
101 * A service is a program that runs in the background on your computer, and has no user interface.
102 *
103 * This is often very handy - and often useful to mix together with a related non-service (UI)
104 * program component.
105 *
106 * This concept exists on both UNIX and Windows, but is implemented differently.
107 *
108 * There are two different dimensions along which you can subtype the behavior of a service application:
109 * (1) the functionality the service provides
110 * (2) the OS service mechanism to use
111 *
112 * Users of this class will themselves address #1, by subclassing from the IApplicationRep, and providing their application
113 * behavior. But users can also select different 'service implementation' strategies by selecting the IServiceIntegrationRep subtype
114 * to pass into the constructor. What service implementation strategies are available will depend on the OS you've built for,
115 * and often on command-line app arguments.
116 *
117 * This class is sort of like a singleton, in that it can be instantiated only once, never copied, etc. But
118 * its unusual for the singleton pattern, since the user must explicitly construct it. The owner controls its lifetime.
119 *
120 * Note that this class provides a convenient wrapper on requesting actions on the underlying service
121 * from EITEHR the process in which the service is run, or from any other process (via IPC - specific to the process
122 * service backend implementation).
123 *
124 * This class is 'final' - since the intended use is to provide polymorphic behavior via the REP classes passed
125 * in to the CTOR.
126 *
127 * Miscellaneous Notes:
128 * o No need for a --daemonize option for --Run-As-Service, because that's essentially
129 * what the --start command does
130 *
131 * \par Example Usage
132 * @see Samples/SimpleService project
133 * \code
134 * struct AppRep_ : Main::IApplicationRep {
135 * AppRep_ () = default;
136 * ...
137 * virtual void MainLoop (const std::function<void()>& startedCB) override
138 * {
139 * .... do your service and wait forever for thread abort
140 * }
141 *
142 * };
143 * ...
144 * Sequence<String> args = Execution::ParseCommandLine (argc, argv);
145 * Main m (make_shared<AppRep_> ());
146 * m.Run (args);
147 * \endcode
148 */
149 class Main final {
150 public:
151 struct ServiceDescription;
152
153 public:
154 class IApplicationRep;
155
156 public:
157 class SimpleIApplicationRepHelper;
158
159 public:
161
162#if qStroika_Foundation_Common_Platform_POSIX
163 public:
164 class BasicUNIXServiceImpl;
165#endif
166
167 public:
169
170 public:
171 class RunNoFrillsService;
172
173#if qStroika_Foundation_Common_Platform_Windows
174 public:
175 class WindowsService;
176#endif
177
178 public:
179 /**
180 * The result type depends on your OS/compilation flags.
181 */
182 static shared_ptr<IServiceIntegrationRep> mkDefaultServiceIntegrationRep ();
183
184 public:
185 /**
186 * Note - besides the obvious, the Main () function also sets signal handlers to point to this objects signal handler.
187 */
188 explicit Main (const shared_ptr<IApplicationRep>& appRep,
189 const shared_ptr<IServiceIntegrationRep>& serviceIntegrationRep = mkDefaultServiceIntegrationRep ());
190 Main () = delete;
191 Main (const Main&) = delete;
192
193 public:
194 ~Main ();
195
196 public:
197 nonvirtual const Main& operator= (const Main&) = delete;
198
199 public:
200 struct CommandArgs;
201
202 public:
203 /**
204 * The caller COULD just call these operations and see if they fail, but the operations have side-effects.
205 * This lets the caller detect the features without the side-effects.
206 */
208 eInstall,
209 eGetServicePID,
210 };
211
212 public:
213 /**
214 * The caller COULD just call these operations and see if they fail, but the operations have side-effects.
215 * This lets the caller detect the features without the side-effects.
216 */
218
219 public:
220 /**
221 * These arguments are command-line arguments. They will also be indirectly passed to the IApplicationRep.
222 * So a typical app main() might be:
223 * TBD
224 *
225 * When this function returns, the calling main() should exit. The caller should also be prepared for
226 * exceptions to be thrown by Run - for example - in case of bad command line arguments.
227 *
228 * Note also - some command line arguments are interpreted as meaning to send messages to another process
229 * hosing the service. While others cause this process to become the process main process, and run until
230 * told to exit.
231 */
232 nonvirtual void Run (const CommandArgs& args, const Streams::OutputStream::Ptr<Characters::Character>& out = nullptr);
233
234 public:
235 /**
236 * Checks the state of the given service. NOTE - this works ACROSS PROCESSES. It can be called
237 * to ask in a controller exe if the serviceMain EXE is running. It also - COULD give the
238 * wrong answer - given races, so use with care.
239 */
240 enum class State : uint8_t {
241 eStopped,
242 eRunning,
243 ePaused, // STOPPED in unix
244 };
245 nonvirtual State GetState () const;
246
247 public:
248 /**
249 * Return 0 if no service running
250 * ????
251 */
252 nonvirtual pid_t GetServicePID () const;
253
254 public:
255 /**
256 * Return non-structured, human readable summary of service status
257 */
258 nonvirtual String GetServiceStatusMessage () const;
259
260 public:
261 /**
262 */
263 nonvirtual void Install ();
264
265 public:
266 /**
267 */
268 nonvirtual void UnInstall ();
269
270 public:
271 /**
272 * RunAsService () will not return until the service has terminated. It runs the service 'MainLoop'.
273 * When that happens, the calling application should exit.
274 *
275 * This should NOT be called directly - except when command line arguments say "Run-As-Service".
276 *
277 * This function / operation WILL FAIL if the service is already running (so this automatically implements
278 * protection against running multiple instances of the same service - singleton pattern.
279 *
280 * If the startup of the service fails due to exception, it will be propagated back to the caller.
281 *
282 * @see RunDirectly
283 */
284 nonvirtual void RunAsService ();
285
286 public:
287 /**
288 * RunDirectly () is mostly a debug-handy/debug-friendly variant of RunAsService().
289 *
290 * RunDirectly () will not return until the program has terminated. It runs the service 'MainLoop'.
291 * When that happens, the calling application should exit.
292 *
293 * When using RunDirectly - other operations like 'status' and 'kill' etc - will NOT function.
294 * This bypasses the backend service mechanism - and just runs the application-specific code (typically
295 * so that can be debugged, but possibly also for testing or other purposes).
296 *
297 * If the startup of the service fails due to exception, it will be propagated back to the caller.
298 *
299 * @see RunAsService
300 */
301 nonvirtual void RunDirectly (const optional<Time::Duration>& runFor = {});
302
303 public:
304 /**
305 */
306 nonvirtual void Start (Time::DurationSeconds timeout = Time::kInfinity);
307
308 public:
309 /**
310 */
311 nonvirtual void Stop (Time::DurationSeconds timeout = Time::kInfinity);
312
313 public:
314 /**
315 * Does a regular stop, but if that doesn't work (by the given timeout), do low-level
316 * force stop and cleanup as best as possible.
317 */
318 nonvirtual void ForcedStop (Time::DurationSeconds timeout);
319
320 public:
321 /**
322 */
323 nonvirtual void Restart (Time::DurationSeconds timeout = Time::kInfinity);
324
325 public:
326 /**
327 * Does ForcedStop(timeout) - ignoring errors if was already stopped, and then starts.
328 * This only fails if it couldn't (even forced) stop the service (ignoring timeout) or if
329 * it fails to restart the service.
330 */
331 nonvirtual void ForcedRestart (Time::DurationSeconds timeout = Time::kInfinity, Time::DurationSeconds unforcedStopTimeout = Time::kInfinity);
332
333 public:
334 /**
335 */
336 virtual void ReReadConfiguration ();
337
338 public:
339 /**
340 */
341 virtual void Pause ();
342
343 public:
344 /**
345 */
346 virtual void Continue ();
347
348 public:
349 /**
350 */
351 nonvirtual ServiceDescription GetServiceDescription () const;
352
353 public:
354 struct CommandNames;
355
356 public:
357 struct CommandOptions;
358
359 private:
360 nonvirtual const IServiceIntegrationRep& GetServiceRep_ () const;
361 nonvirtual IServiceIntegrationRep& GetServiceRep_ ();
362 nonvirtual const IApplicationRep& GetAppRep_ () const;
363 nonvirtual IApplicationRep& GetAppRep_ ();
364
365 private:
366 shared_ptr<IServiceIntegrationRep> fServiceRep_; // no need to synchronize because all access to shared_ptr R/O after initialization
367 };
368
369 /**
370 */
371 struct Main::ServiceDescription {
372 String fRegistrationName; // key name used when registering service with OS
373 String fPrettyName; // pretty printed version of fRegistrationName (can be same)
374 };
375
376 /**
377 */
378 struct Main::CommandNames {
379
380 /**
381 */
382 static inline constexpr string_view kInstall = "Install"sv;
383
384 /**
385 */
386 static inline constexpr string_view kUnInstall = "UnInstall"sv;
387
388 /**
389 * The kRunAsService command is about the only command that tends to NOT be called by users on the command line.
390 * it tells the code to run indefinitely, (until told to stop) - running the service loop.
391 *
392 * This is typically called INDRECTLY via a special fork/exec as a result of a kStart command, or its called from
393 * init as part of system startup.
394 */
395 static inline constexpr string_view kRunAsService = "Run-As-Service"sv;
396
397 /**
398 * kRunDirectly is mostly a debug-handy/debug-friendly variant of RunAsService().
399 *
400 * kRunDirectly will not return until the program has terminated. It runs the service 'MainLoop'.
401 * When that happens, the calling application should exit.
402 *
403 * When using kRunDirectly - other operations like 'status' and 'kill' etc - will NOT function.
404 * This bypasses the backend service mechanism - and just runs the application-specific code (typically
405 * so that can be debugged, but possibly also for testing or other purposes).
406 */
407 static inline constexpr string_view kRunDirectly = "Run-Directly"sv;
408
409 //static inline constexpr string_view kRunDirectlyFor = "Run-Directly-For"sv;
410
411 /*
412 * The kStart command tells the service to start running. It returns an error
413 * if the service is already started.
414 */
415 static inline constexpr string_view kStart = "Start"sv;
416
417 /**
418 * The kStop command tells the service to start terminate
419 */
420 static inline constexpr string_view kStop = "Stop"sv;
421
422 //DOCUMENT EACH
423 //NEATLY
424 // KILL termiantes (kill-9)
425 //
426 static inline constexpr string_view kForcedStop = "ForcedStop"sv;
427
428 // restart synonym for stop (no error if not already running), and then start
429 static inline constexpr string_view kRestart = "Restart"sv;
430
431 static inline constexpr string_view kForcedRestart = "ForcedRestart"sv;
432
433 // If service knows how to find its own config files - recheck them
434 static inline constexpr string_view kReloadConfiguration = "Reload-Configuration"sv;
435
436 // SIGSTOP
437 static inline constexpr string_view kPause = "Pause"sv;
438
439 // SIGCONT
440 static inline constexpr string_view kContinue = "Continue"sv;
441
442 static inline constexpr string_view kRunFor = "runFor"sv;
443 static inline constexpr string_view kStatus = "Status"sv;
444 };
445
446 struct Main::CommandOptions {
447 static inline const Execution::CommandLine::Option kInstall{.fLongName = CommandNames::kInstall,
448 .fHelpOptionText = "Register the service as installed"sv};
449 static inline const Execution::CommandLine::Option kUnInstall{.fLongName = CommandNames::kUnInstall,
450 .fHelpOptionText = "Unregister the service installation"sv};
451 static inline const Execution::CommandLine::Option kRunAsService{
452 .fLongName = CommandNames::kRunAsService, .fHelpOptionText = "Run as a service daemon - as of run by OS service manager"sv};
453 static inline const Execution::CommandLine::Option kRunDirectly{.fLongName = CommandNames::kRunDirectly};
454 // static inline const Execution::CommandLine::Option kRunDirectlyFor{.fLongName = CommandNames::kRunDirectlyFor, .fSupportsArgument = true};
455 static inline const Execution::CommandLine::Option kStart{.fLongName = CommandNames::kStart,
456 .fHelpOptionText = "Request OS service manager start the service."sv};
457 static inline const Execution::CommandLine::Option kStop{.fLongName = CommandNames::kStop,
458 .fHelpOptionText = "Request service manager stop the running service."sv};
459 static inline const Execution::CommandLine::Option kForcedStop{
460 .fLongName = CommandNames::kForcedStop,
461 .fHelpOptionText = "Request service manager stop the running service (aggressively if it doesn't respond)."sv};
462 static inline const Execution::CommandLine::Option kRestart{.fLongName = CommandNames::kRestart, .fHelpOptionText = "Stop followed by Start."s};
463 static inline const Execution::CommandLine::Option kForcedRestart{.fLongName = CommandNames::kForcedRestart,
464 .fHelpOptionText = "ForcedStop followed by Start."s};
465 static inline const Execution::CommandLine::Option kReloadConfiguration{.fLongName = CommandNames::kReloadConfiguration,
466 .fHelpOptionText = "Request service reload its configuration."sv};
467 static inline const Execution::CommandLine::Option kPause{.fLongName = CommandNames::kPause,
468 .fHelpOptionText = "Request OS service manager pause a service."sv};
469 static inline const Execution::CommandLine::Option kContinue{.fLongName = CommandNames::kContinue,
470 .fHelpOptionText = "Request OS service manager continue a paused service."sv};
471
472 static inline const Execution::CommandLine::Option kRunFor{.fLongName = CommandNames::kRunFor,
473 .fSupportsArgument = true,
474 .fHelpArgName = "NSECONDS"sv,
475 .fHelpOptionText = "Run for the argument number of seconds, and quietly quit."sv};
476 static inline const Execution::CommandLine::Option kStatus{.fLongName = CommandNames::kStatus,
477 .fHelpOptionText = "Check the status of the running service"sv};
478
479 static inline const initializer_list<Execution::CommandLine::Option> kAll{
480 kInstall, kUnInstall, kRunAsService, kRunDirectly, kStart, kStop, kForcedStop,
481 kRestart, kForcedRestart, kReloadConfiguration, kPause, kContinue, kRunFor, kStatus};
482 };
483
484 /**
485 * This is helpful to take command-line arguments and produce a set of things todo
486 * for the service mgr. This constructor will throw if it sees something obviously wrong
487 * but will ignore unrecognized arguments.
488 */
490 /**
491 */
492 CommandArgs (const Execution::CommandLine& cmdLine);
493
494 enum class MajorOperation {
495 eInstall,
496 eUnInstall,
497 eRunServiceMain,
498 eRunDirectly, // uses fRunFor if provided
499 eStart,
500 eStop,
501 eForcedStop,
502 eRestart,
503 eForcedRestart,
504 eReloadConfiguration,
505 ePause,
506 eContinue,
507 };
508
509 /**
510 */
511 optional<MajorOperation> fMajorOperation;
512
513 /**
514 * If provided, how long to run for in RunDirectly commands, else treat as run infinitely
515 */
516 optional<Time::Duration> fRunFor;
517
518 /**
519 */
520 Sequence<String> fUnusedArguments;
521 };
522
523 /**
524 * To use this interface users of the Framework::Service module must implement (to represent the running service).
525 *
526 * MainLoop () is automatically setup to run on its own thread. Beware, the OnXXX
527 * events maybe called on this object, but from any thread so be careful of thread safety!
528 */
530 public:
531 IApplicationRep () = default;
532 IApplicationRep (const IApplicationRep&) = delete;
533 virtual ~IApplicationRep () = default;
534
535 public:
536 nonvirtual const IApplicationRep& operator= (const IApplicationRep&) = delete;
537
538 public:
539 /**
540 * Each command line argument is first passed to the IApplicationRep, and it returns true if it handled it
541 * and default for the default behavior.
542 *
543 * Note - for multi-argument sequences, the implementer must set state so its prepared for the next
544 * argument.
545 *
546 * This function MAY raise an exception if it receives unrecognized arguments, and should not
547 * print an error message in that case.
548 */
549 virtual bool HandleCommandLineArgument (const String& s);
550
551 public:
552 /**
553 * This should be overridden by each service, and should never return until the service is done
554 * (probably because of a stop request).
555 *
556 * Each MainLoop() runs on a thread, and it is terminated by being sent a Thread::AbortException.
557 *
558 * This is handy, because its mostly treated by Stroika classes automatically, and mostly automatically
559 * cleans up any ongoing operations.
560 *
561 * It might be written as:
562 * ...
563 * [[maybe_unused]] auto&& cleanup = Execution::Finally ([this] () {
564 * // do your cleanup here
565 * });
566 * // INITIALIZE_SOMETHING(); - maybe firing off other threads
567 * startedCallback (); // notify the service subsystem you've successfully started your service
568 * Execution::Event w;
569 * w.Wait (); // wait til your told your done. This could be replaced with real work, but need
570 * // not be - if you do you real work on another thread
571 * return;
572 *
573 * See the Samples/SimpleService example code for a functional example.
574 */
575 virtual void MainLoop (const function<void ()>& startedCB) = 0;
576
577 public:
578 virtual void OnReReadConfigurationRequest ();
579
580 public:
581 // returns a readable string about the service status. Note most of this is done by the envelope class, and this is just a way to add
582 // service specific extras
583 virtual String GetServiceStatusMessage () const;
584
585 public:
586 virtual ServiceDescription GetServiceDescription () const = 0;
587 };
588
589 /**
590 */
591 class Main::SimpleIApplicationRepHelper : public Main::IApplicationRep {
592 public:
593 SimpleIApplicationRepHelper () = default;
594 };
595
596 /**
597 * \note These CAN be accessed from multiple threads, and each subclass respecting this API
598 * must be internally synchronized.
599 */
601 public:
602 IServiceIntegrationRep () = default;
604
605 public:
606 virtual ~IServiceIntegrationRep () = default;
607
608 public:
609 nonvirtual const IServiceIntegrationRep& operator= (const IServiceIntegrationRep&) = delete;
610
611 protected:
612 /**
613 * Only legal to attach if _GetAttachedAppRep() == nullptr, and only legal to detach if _GetAttachedAppRep () != nullptr.
614 *
615 * Must be attached before other methods can be called (at least most - document which).
616 *
617 * Must be detached before DTOR.
618 */
619 virtual void _Attach (const shared_ptr<IApplicationRep>& appRep) = 0;
620
621 protected:
622 /**
623 */
624 virtual shared_ptr<IApplicationRep> _GetAttachedAppRep () const = 0;
625
626 protected:
627 virtual Containers::Set<ServiceIntegrationFeatures> _GetSupportedFeatures () const = 0;
628
629 protected:
630 virtual State _GetState () const = 0;
631
632 protected:
633 /**
634 * Each command line argument is first passed to the IApplicationRep, and it returns true if it handled it
635 * and default for the default behavior.
636 *
637 * Note - for multi-argument sequences, the implementer must set state so its prepared for the next
638 * argument.
639 *
640 * This function MAY raise an exception if it receives unrecognized argumetns, and shoudl not
641 * print an error message in that case.
642 */
643 virtual bool HandleCommandLineArgument (const String& s);
644
645 protected:
646 /**
647 * (only supported if (need service supports install-feature)
648 */
649 virtual void _Install () = 0;
650
651 protected:
652 /**
653 * (only supported if (need service supports uninstall-feature)
654 */
655 virtual void _UnInstall () = 0;
656
657 protected:
658 /**
659 */
660 virtual void _RunAsService () = 0;
661
662 protected:
663 /**
664 */
665 virtual void _RunDirectly (const optional<Time::Duration>& runFor) = 0;
666
667 protected:
668 /**
669 */
670 virtual void _Start (Time::DurationSeconds timeout) = 0;
671
672 protected:
673 /**
674 */
675 virtual void _Stop (Time::DurationSeconds timeout) = 0;
676
677 protected:
678 /**
679 */
680 virtual void _ForcedStop (Time::DurationSeconds timeout) = 0;
681
682 protected:
683 /**
684 */
685 virtual pid_t _GetServicePID () const = 0;
686
687 private:
688 friend class Main;
689 };
690
691 /**
692 * Wrap this around any IServiceIntegrationRep, to get Logging to work.
693 *
694 * \note - LoggerServiceWrapper doesn't work with WindowsService to get logging on _RunAsService cuz we don't get a hook
695 * http://stroika-bugs.sophists.com/browse/STK-476
696 */
698 public:
699 LoggerServiceWrapper (const shared_ptr<Main::IServiceIntegrationRep>& delegateTo);
700
701 protected:
702 virtual void _Attach (const shared_ptr<IApplicationRep>& appRep) override;
703 virtual shared_ptr<IApplicationRep> _GetAttachedAppRep () const override;
704 virtual Containers::Set<ServiceIntegrationFeatures> _GetSupportedFeatures () const override;
705 virtual State _GetState () const override;
706 virtual void _Install () override;
707 virtual void _UnInstall () override;
708 virtual void _RunAsService () override;
709 virtual void _RunDirectly (const optional<Time::Duration>& runFor) override;
710 virtual void _Start (Time::DurationSeconds timeout) override;
711 virtual void _Stop (Time::DurationSeconds timeout) override;
712 virtual void _ForcedStop (Time::DurationSeconds timeout) override;
713 virtual pid_t _GetServicePID () const override;
714
715 private:
716 shared_ptr<Main::IServiceIntegrationRep> fDelegateTo_; // no need to synchronize because all access to shared_ptr R/O after initialization
717 };
718
719 /**
720 * Run with absolutely minimal OS integration support. Count on the app itself to make service calls
721 * to start/stop
722 */
724 protected:
725 virtual void _Attach (const shared_ptr<IApplicationRep>& appRep) override;
726 virtual shared_ptr<IApplicationRep> _GetAttachedAppRep () const override;
727 virtual Containers::Set<ServiceIntegrationFeatures> _GetSupportedFeatures () const override;
728 virtual State _GetState () const override;
729 virtual void _Install () override;
730 virtual void _UnInstall () override;
731 virtual void _RunAsService () override;
732 virtual void _RunDirectly (const optional<Time::Duration>& runFor) override;
733 virtual void _Start (Time::DurationSeconds timeout) override;
734 virtual void _Stop (Time::DurationSeconds timeout) override;
735 virtual void _ForcedStop (Time::DurationSeconds timeout) override;
736 virtual pid_t _GetServicePID () const override;
737
738 private:
739 shared_ptr<IApplicationRep> fAppRep_;
740 };
741
742#if qStroika_Foundation_Common_Platform_POSIX
743 /**
744 * Default for UNIX - responds in standard way to basic signals etc
745 */
746 class Main::BasicUNIXServiceImpl : public Main::IServiceIntegrationRep {
747 public:
748 BasicUNIXServiceImpl ();
749 ~BasicUNIXServiceImpl ();
750
751 protected:
752 virtual void _Attach (const shared_ptr<IApplicationRep>& appRep) override;
753 virtual shared_ptr<IApplicationRep> _GetAttachedAppRep () const override;
754 virtual Containers::Set<ServiceIntegrationFeatures> _GetSupportedFeatures () const override;
755 virtual State _GetState () const override;
756 virtual void _Install () override;
757 virtual void _UnInstall () override;
758 virtual void _RunAsService () override;
759 virtual void _RunDirectly (const optional<Time::Duration>& runFor) override;
760 virtual void _Start (Time::DurationSeconds timeout) override;
761 virtual void _Stop (Time::DurationSeconds timeout) override;
762 virtual void _ForcedStop (Time::DurationSeconds timeout) override;
763 virtual pid_t _GetServicePID () const override;
764
765 protected:
766 virtual filesystem::path _GetPIDFileName () const;
767
768 protected:
769 // Called internally when - for example - asked to start and we find there are already lock files etc from
770 // a previous run of the service, but its actually dead
771 virtual void _CleanupDeadService ();
772
773 private:
774 nonvirtual void SetupSignalHanlders_ (bool install);
775
776 /*
777 * By default, ServiceMain sets up its own signal handlers for
778 *
779 * SIGTERM
780 * SIGHUP
781 * ....
782 *<<should add more - like TSTP and CONT - but not high priorities since the default UNIX behavior of these is pretty reasonable
783 *>>>--LGP 2011-09-24
784 *
785 * If the user of this class needs there own signal handlers, but still wnats to leverage the default handling in this
786 * class, there are two easy ways:
787 * (1) overide the 'rep' method Signalhandler and delegate t your own handlers.
788 * (2) or, replace the signal hanlder yourself (with the signal system call), and call
789 * SignalHandler () directly on this class.
790 */
791 public:
792 static constexpr SignalID kSIG_ReReadConfiguration = SIGHUP;
793
794 private:
795 Execution::SignalHandler fOurSignalHandler_; // only initialized and then read as consant, so no need to synchronize
796 nonvirtual void SignalHandler_ (SignalID signum);
797
798 // MUST REDO THIS STUFF WITH EVENTS - when we have POSIX complaint event support in Stroika Foundation
799 protected:
800 nonvirtual bool _CheckShouldReReadConfig () const;
801 nonvirtual void _DidReReadConfig ();
802
803 private:
804 bool fMustReReadConfig{false};
805
806 private:
809 };
810#endif
811
812#if qStroika_Foundation_Common_Platform_Windows
813 /**
814 * Run as a windows service - integrating with the Windows Service Mgr
815 *
816 * \note - LoggerServiceWrapper doesn't work with WindowsService to get logging on _RunAsService cuz we don't get a hook
817 * http://stroika-bugs.sophists.com/browse/STK-476
818 */
819 class Main::WindowsService : public Main::IServiceIntegrationRep {
820 public:
821 WindowsService ();
822
823 protected:
824 virtual void _Attach (const shared_ptr<IApplicationRep>& appRep) override;
825 virtual shared_ptr<IApplicationRep> _GetAttachedAppRep () const override;
826 virtual Containers::Set<ServiceIntegrationFeatures> _GetSupportedFeatures () const override;
827 virtual State _GetState () const override;
828 virtual void _Install () override;
829 virtual void _UnInstall () override;
830 virtual void _RunAsService () override;
831 virtual void _RunDirectly (const optional<Time::Duration>& runFor) override;
832 virtual void _Start (Time::DurationSeconds timeout) override;
833 virtual void _Stop (Time::DurationSeconds timeout) override;
834 virtual void _ForcedStop (Time::DurationSeconds timeout) override;
835 virtual pid_t _GetServicePID () const override;
836
837 private:
838 nonvirtual Characters::SDKString GetSvcName_ () const;
839 nonvirtual bool IsInstalled_ () const noexcept;
840 nonvirtual void SetServiceStatus_ (DWORD dwState) noexcept;
841 nonvirtual void ServiceMain_ (DWORD dwArgc, LPTSTR* lpszArgv) noexcept;
842 static void WINAPI StaticServiceMain_ (DWORD dwArgc, LPTSTR* lpszArgv) noexcept;
843 nonvirtual void Handler_ (DWORD dwOpcode) noexcept;
844 static void WINAPI StaticHandler_ (DWORD dwOpcode) noexcept;
845 nonvirtual void OnStopRequest_ () noexcept;
846
847 private:
848 static WindowsService* s_SvcRunningTHIS_;
849 Execution::Thread::Ptr fRunThread_;
850 SERVICE_STATUS_HANDLE fServiceStatusHandle_{nullptr}; // nullptr if invalid - not INVALID_HANDLE
851 SERVICE_STATUS fServiceStatus_{};
852 shared_ptr<IApplicationRep> fAppRep_;
853 };
854#endif
855}
856
857/*
858 ********************************************************************************
859 ***************************** Implementation Details ***************************
860 ********************************************************************************
861 */
862#include "Main.inl"
863
864#endif /*_Stroika_Frameworks_Service_Main_h_*/
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Definition Sequence.h:187
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Definition Set.h:105
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
virtual void MainLoop(const function< void()> &startedCB)=0
virtual void _Attach(const shared_ptr< IApplicationRep > &appRep)=0
virtual void _Attach(const shared_ptr< IApplicationRep > &appRep) override
virtual void _Attach(const shared_ptr< IApplicationRep > &appRep) override
static shared_ptr< IServiceIntegrationRep > mkDefaultServiceIntegrationRep()
nonvirtual Containers::Set< ServiceIntegrationFeatures > GetServiceIntegrationFeatures() const
Definition Main.inl:32
nonvirtual pid_t GetServicePID() const
Definition Main.inl:74
nonvirtual void RunDirectly(const optional< Time::Duration > &runFor={})
nonvirtual void ForcedRestart(Time::DurationSeconds timeout=Time::kInfinity, Time::DurationSeconds unforcedStopTimeout=Time::kInfinity)
nonvirtual void ForcedStop(Time::DurationSeconds timeout)
Definition Main.inl:66
nonvirtual void Run(const CommandArgs &args, const Streams::OutputStream::Ptr< Characters::Character > &out=nullptr)
nonvirtual String GetServiceStatusMessage() const
basic_string< SDKChar > SDKString
Definition SDKString.h:38
int pid_t
TODO - maybe move this to configuraiotn module???
Definition Module.h:34