Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Samples/Service/Sources/Main.cpp
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <cstdlib>
7#include <iostream>
8
10#include "Stroika/Foundation/Debug/Fatal.h"
12#include "Stroika/Foundation/Execution/CommandLine.h"
14#include "Stroika/Foundation/Execution/SignalHandlers.h"
16#if qStroika_Foundation_Common_Platform_Windows
17#include "Stroika/Foundation/Execution/Platform/Windows/Exception.h"
18#include "Stroika/Foundation/Execution/Platform/Windows/StructuredException.h"
19#endif
21#include "Stroika/Frameworks/Service/Main.h"
22
23#include "AppVersion.h"
24
25#include "Service.h"
26
27/**
28 * \file
29 *
30 * SAMPLE CODE
31 *
32 * Sample Simple Service Application
33 *
34 * This sample demonstrates a few Stroika features.
35 *
36 * o Creating a service application (one that can be automatically started/stopped by
37 * the OS, and one where you can query the status, check process ID, etc)
38 *
39 * o Simple example of command line processing
40 *
41 * o Simple example of Logging (to syslog or windows log or other)
42 */
43
44using namespace std;
45
46using namespace Stroika::Foundation;
48using namespace Stroika::Foundation::Execution;
49using namespace Stroika::Frameworks::Service;
50
52
53#if qUseLogger
57#endif
58
59namespace {
61 {
62 if (not e.fMessage.empty ()) {
63 cerr << "Error: " << e.fMessage.AsUTF8<string> () << endl;
64 cerr << endl;
65 }
66 cerr << "Usage: " << m.GetServiceDescription ().fRegistrationName.AsNarrowSDKString () << " [options] where options can be :\n ";
67 if (m.GetServiceIntegrationFeatures ().Contains (Main::ServiceIntegrationFeatures::eInstall)) {
68 cerr << "\t--" << String{Main::CommandNames::kInstall}.AsNarrowSDKString ()
69 << " /* Install service (only when debugging - should use real installer like WIX) */" << endl;
70 cerr << "\t--" << String{Main::CommandNames::kUnInstall}.AsNarrowSDKString ()
71 << " /* UnInstall service (only when debugging - should use real installer like WIX) */" << endl;
72 }
73 cerr << "\t--" << String{Main::CommandNames::kRunAsService}.AsNarrowSDKString ()
74 << " /* Run this process as a service (doesn't exit until the serivce is done ...) */" << endl;
75 cerr << "\t--" << String{Main::CommandNames::kRunDirectly}.AsNarrowSDKString () << " /* Run this process as a directly (doesn't exit until the serivce is done or ARGUMENT TIMEOUT seconds elapsed ...) but not using service infrastructure */"
76 << endl;
77 cerr << "\t--" << String{Main::CommandNames::kStart}.AsNarrowSDKString ()
78 << " /* Service/Control Function: Start the service */" << endl;
79 cerr << "\t--" << String{Main::CommandNames::kStop}.AsNarrowSDKString ()
80 << " /* Service/Control Function: Stop the service */" << endl;
81 cerr << "\t--" << String{Main::CommandNames::kForcedStop}.AsNarrowSDKString ()
82 << " /* Service/Control Function: Forced stop the service (after trying to normally stop) */" << endl;
83 cerr << "\t--" << String{Main::CommandNames::kRestart}.AsNarrowSDKString ()
84 << " /* Service/Control Function: Stop and then re-start the service (ok if already stopped) */" << endl;
85 cerr << "\t--" << String{Main::CommandNames::kForcedRestart}.AsNarrowSDKString ()
86 << " /* Service/Control Function: Stop (force if needed) and then re-start the service (ok if already stopped) */" << endl;
87 cerr << "\t--" << String{Main::CommandNames::kReloadConfiguration}.AsNarrowSDKString () << " /* Reload service configuration */" << endl;
88 cerr << "\t--" << String{Main::CommandNames::kPause}.AsNarrowSDKString ()
89 << " /* Service/Control Function: Pause the service */" << endl;
90 cerr << "\t--" << String{Main::CommandNames::kContinue}.AsNarrowSDKString ()
91 << " /* Service/Control Function: Continue the paused service */" << endl;
92 cerr << "\t--Status /* Service/Control Function: Print status of running service */ " << endl;
93 cerr << "\t--Version /* print this application version */ " << endl;
94 cerr << "\t--help /* Print this help. */ " << endl;
95 cerr << endl
96 << "\tExtra unrecognized parameters for start/restart, and forcedrestart operations will be passed along to the actual "
97 "service process"
98 << endl;
99 cerr << endl;
100 }
101}
102
103int main (int argc, const char* argv[])
104{
105 CommandLine cmdLine{argc, argv};
106 Debug::TraceContextBumper ctx{"main", "argv={}"_f, cmdLine};
107
108#if qStroika_Foundation_Execution_Thread_SupportThreadStatistics
109 [[maybe_unused]] auto&& cleanupReport = Execution::Finally (
110 [] () { DbgTrace ("Exiting main with thread {} running"_f, Execution::Thread::GetStatistics ().fRunningThreads); });
111#endif
112
113 /*
114 * This allows for safe signals to be managed in a threadsafe way
115 */
117
118 /*
119 * Setup basic (optional) error handling.
120 */
121#if qStroika_Foundation_Common_Platform_Windows
124#endif
125 Debug::RegisterDefaultFatalErrorHandlers (Execution::DefaultLoggingFatalErrorHandler);
126
127 /*
128 * SetStandardCrashHandlerSignals not really needed, but helpful for many applications so you get a decent log message/debugging on crash.
129 */
131
132 /*
133 * Ignore SIGPIPE is common practice/helpful in POSIX, but not required by the service manager.
134 */
135#if qStroika_Foundation_Common_Platform_POSIX
137#endif
138
139 /*
140 * Setup Logging to the OS logging facility.
141 */
142#if qUseLogger
143 /*
144 * Optional - use buffering feature
145 * Optional - use suppress duplicates in a 15 second window
146 */
147 Logger::Activator loggerActivation{Logger::Options{
148 .fLogBufferingEnabled = true,
149 .fSuppressDuplicatesThreshold = 15s,
150 }};
151 bool dockerContainerFlag = false; // get from command-line???
152 if (dockerContainerFlag) {
153 using namespace IO::FileSystem;
154 Logger::sThe.AddAppender (
155 make_shared<Logger::StreamAppender> (FileOutputStream::New (STDOUT_FILENO, FileStream::AdoptFDPolicy::eDisconnectOnDestruction)));
156 }
157 else {
158#if qStroika_HasComponent_syslog
159 Logger::sThe.SetAppenders (make_shared<Logger::SysLogAppender> ("Stroika-Sample-Service"sv));
160#elif qStroika_Foundation_Common_Platform_Windows
161 Logger::sThe.SetAppenders (make_shared<Logger::WindowsEventLogAppender> ("Stroika-Sample-Service"sv));
162#endif
163 }
164#endif
165
166 /*
167 * Parse command line arguments, and start looking at options.
168 */
169 shared_ptr<Main::IServiceIntegrationRep> serviceIntegrationRep = Main::mkDefaultServiceIntegrationRep ();
170#if qUseLogger
171 serviceIntegrationRep = make_shared<Main::LoggerServiceWrapper> (serviceIntegrationRep);
172#endif
173
174 /*
175 * Create service handler instance.
176 */
177 Main m{make_shared<Samples::Service::SampleAppServiceRep> (), serviceIntegrationRep};
178
179 /*
180 * Run request.
181 */
182 try {
184 using Execution::StandardCommandLineOptions::kHelp;
185 using Execution::StandardCommandLineOptions::kVersion;
186
187 Sequence<CommandLine::Option> allMyOptions =
188 Sequence<CommandLine::Option>{Main::CommandOptions::kAll} + Sequence<CommandLine::Option>{kHelp, kVersion};
189 cmdLine.Validate (allMyOptions);
190
191 if (cmdLine.Has (Main::CommandOptions::kStatus)) {
192 cout << m.GetServiceStatusMessage ().AsUTF8<string> ();
193 return EXIT_SUCCESS;
194 }
195 else if (cmdLine.Has (kHelp)) {
196 ShowUsage_ (m);
197 return EXIT_SUCCESS;
198 }
199 else if (cmdLine.Has (kVersion)) {
200 cout << m.GetServiceDescription ().fPrettyName.AsNarrowSDKString () << ": "sv
201 << Characters::ToString (AppVersion::kVersion).AsNarrowSDKString () << endl;
202 return EXIT_SUCCESS;
203 }
204 else {
205 /*
206 * Run the commands, and capture/display exceptions
207 */
208 m.Run (cmdLine);
209 }
210 }
212 ShowUsage_ (m, e);
213 }
214 catch (...) {
215 String exceptMsg = Characters::ToString (current_exception ());
216#if qUseLogger
217 Logger::sThe.Log (Logger::eError, "{}"_f, exceptMsg);
218#endif
219 cerr << "FAILED: " << exceptMsg.AsNarrowSDKString () << endl;
220 return EXIT_FAILURE;
221 }
222 return EXIT_SUCCESS;
223}
#define DbgTrace
Definition Trace.h:309
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual string AsNarrowSDKString() const
Definition String.inl:834
A generalization of a vector: a container whose elements are keyed by the natural numbers.
A simple/portable wrapper on syslog/log4j/WindowsEventlog, with features like throttling,...
Definition Logger.h:94
nonvirtual void SetStandardCrashHandlerSignals(SignalHandler handler=SignalHandler{DefaultCrashSignalHandler, SignalHandler::Type::eDirect}, const Containers::Set< SignalID > &forSignals=GetStandardCrashSignals())
static shared_ptr< IServiceIntegrationRep > mkDefaultServiceIntegrationRep()
nonvirtual Containers::Set< ServiceIntegrationFeatures > GetServiceIntegrationFeatures() const
Definition Main.inl:32
nonvirtual void Run(const CommandArgs &args, const Streams::OutputStream::Ptr< Characters::Character > &out=nullptr)
void DefaultLoggingCrashSignalHandler(Execution::SignalID signal) noexcept
Definition Logger.cpp:602
STL namespace.