45 unique_ptr<RequestHandler> requestHandlerPtr;
46 optional<string> configFile;
47 atomic<bool> readyToReconfigure(
false);
49 unsigned int terminationTimeout = 10;
52 void shutdown(
int signum) {
53 NLOG_INFO(logger,
"Terminating on signal " << signum)
56 if (requestHandlerPtr) {
58 requestHandlerPtr->stop();
61 sleep(terminationTimeout);
62 if (requestHandlerPtr && signum != SIGUSR1) {
63 NLOG_INFO(logger,
"Enforcing termination now, ignoring pending requests.")
64 requestHandlerPtr->terminate();
72 void* loadAppSymbol(
void* appOpen,
char const* symbolName,
string const& error) {
73 void* symbol = dlsym(appOpen, symbolName);
74 auto dlsymError = dlerror();
76 throw Exception(__FUNCTION__, 11, error, dlsymError);
82 void closeApp(
void* appOpen) {
90 void setTerminationTimeout(
Config const& config) {
91 string terminationTimeoutStr = config[{
"system",
"termination_timeout"}];
92 if (!terminationTimeoutStr.empty()) {
94 auto newTerminationTimeout = stoul(terminationTimeoutStr);
95 terminationTimeout = newTerminationTimeout;
96 }
catch (invalid_argument& e) {
97 NLOG_WARNING(logger,
"WARNING: Invalid termination timeout given in configuration, default value "
98 "or previous value will be used.")
110 return Config(*configFile);
114 NLOG_WARNING(logger, "WARNING: App will not be reloaded as well")
130 auto const& [uid, gid, supplementaryGroups] = data;
131 if (uid != 0 && gid != 0 && setgroups(supplementaryGroups.size(), &supplementaryGroups[0]) != 0) {
132 NLOG_ERROR(logger,
"Fatal Error: Could not set supplementary groups during privilege downgrade.")
135 if (setgid(gid) != 0 || setuid(uid) != 0) {
136 NLOG_ERROR(logger,
"Fatal Error: Could not set privileges during privilege downgrade.")
141 void printHelpAndExit() {
142 cout <<
"nawarun is the runner for NAWA web applications.\n\n"
143 "Usage: nawarun [<overrides>] [<config-file> | --no-config-file]\n\n"
144 "Format for configuration overrides: --<category>:<key>=<value>\n\n"
145 "If no config file is given, nawarun will try to use config.ini from the current\n"
146 "working directory, unless the --no-config-file option is given. The config file\n"
147 "as well as --no-config-file are only accepted as the last command line argument\n"
148 "after the overrides.\n\n"
154 void printVersionAndExit() {
159 void setUpSignalHandlers() {
160 signal(SIGINT, shutdown);
161 signal(SIGTERM, shutdown);
162 signal(SIGUSR1, shutdown);
166 void setUpLogging(
Config const& config) {
167 auto configuredLogLevel = config[{
"logging",
"level"}];
168 if (configuredLogLevel ==
"off") {
169 Log::setOutputLevel(Log::Level::OFF);
170 }
else if (configuredLogLevel ==
"error") {
171 Log::setOutputLevel(Log::Level::ERROR);
172 }
else if (configuredLogLevel ==
"warning") {
173 Log::setOutputLevel(Log::Level::WARNING);
174 }
else if (configuredLogLevel ==
"debug") {
175 Log::setOutputLevel(Log::Level::DEBUG);
177 if (config[{
"logging",
"extended"}] ==
"on") {
178 Log::setExtendedFormat(
true);
187 cReal = config.
isSet({
"system",
"threads"})
188 ? stod(config[{
"system",
"threads"}])
190 }
catch (invalid_argument& e) {
191 NLOG_WARNING(logger,
"WARNING: Invalid value given for system/concurrency given in the config file.")
194 if (config[{
"system",
"concurrency"}] ==
"hardware") {
195 cReal = max(1.0, thread::hardware_concurrency() * cReal);
197 return static_cast<unsigned int>(cReal);
202 string appPath = config[{
"application",
"path"}];
203 if (appPath.empty()) {
204 throw Exception(__FUNCTION__, 1,
"Application path not set in config file.");
206 void* appOpen = dlopen(appPath.c_str(), RTLD_LAZY);
208 throw Exception(__FUNCTION__, 2,
"Application file could not be loaded.", dlerror());
217 string appVersionError =
"Could not read nawa version from application.";
218 auto appNawaVersionMajor = (
int*) loadAppSymbol(appOpen,
"nawa_version_major", appVersionError);
219 auto appNawaVersionMinor = (
int*) loadAppSymbol(appOpen,
"nawa_version_minor", appVersionError);
221 throw Exception(__FUNCTION__, 3,
"App has been compiled against another version of NAWA.");
223 auto appInit = (
init_t*) loadAppSymbol(appOpen,
"init",
"Could not load init function from application.");
224 auto appHandleRequest = (
handleRequest_t*) loadAppSymbol(appOpen,
"handleRequest",
225 "Could not load handleRequest function from application.");
226 return {appInit, make_shared<HandleRequestFunctionWrapper>(appHandleRequest, appOpen, closeApp)};
231 NLOG_WARNING(logger,
"WARNING: Reloading is not supported without config file and will therefore not "
236 if (requestHandlerPtr && readyToReconfigure) {
237 NLOG_INFO(logger,
"Reloading config and app on signal " << signum)
238 readyToReconfigure =
false;
242 readyToReconfigure =
true;
247 setTerminationTimeout(*config);
250 shared_ptr<HandleRequestFunctionWrapper> appHandleRequest;
256 NLOG_WARNING(logger,
"WARNING: Configuration will be reloaded anyway")
259 requestHandlerPtr->reconfigure(nullopt, nullopt, config);
260 readyToReconfigure =
true;
266 auto initReturn = appInit(appInitStruct);
269 if (initReturn != 0) {
271 "ERROR: App init function returned " << initReturn <<
" -- cancelling reload of app.")
272 NLOG_WARNING(logger,
"WARNING: Configuration will be reloaded anyway")
275 requestHandlerPtr->reconfigure(nullopt, nullopt, config);
276 readyToReconfigure =
true;
281 requestHandlerPtr->reconfigure(appHandleRequest, appInitStruct.
accessFilters(), appInitStruct.
config());
282 readyToReconfigure =
true;
288 auto initialUID = getuid();
291 vector<gid_t> supplementaryGroups;
293 if (initialUID == 0) {
294 if (!config.
isSet({
"privileges",
"user"}) || !config.
isSet({
"privileges",
"group"})) {
296 "Running as root and user or group for privilege downgrade is not set in the configuration.");
298 string username = config[{
"privileges",
"user"}];
299 string groupname = config[{
"privileges",
"group"}];
302 privUser = getpwnam(username.c_str());
303 privGroup = getgrnam(groupname.c_str());
304 if (privUser ==
nullptr || privGroup ==
nullptr) {
306 "The user or group name for privilege downgrade given in the configuration is invalid.");
308 privUID = privUser->pw_uid;
309 privGID = privGroup->gr_gid;
310 if (privUID == 0 || privGID == 0) {
311 NLOG_WARNING(logger,
"WARNING: nawarun will be running as user or group root. Security risk!")
315 getgrouplist(username.c_str(), privGID,
nullptr, &n);
316 supplementaryGroups.resize(n, 0);
318 NLOG_WARNING(logger,
"WARNING: Could not get supplementary groups for user " << username)
319 supplementaryGroups = {privGID};
322 return make_tuple(privUID, privGID, supplementaryGroups);
325 NLOG_WARNING(logger,
"WARNING: Not starting as root, cannot set privileges.")
343 optional<string> configPath;
344 vector<pair<pair<string, string>,
string>> overrides;
345 bool noConfigFile =
false;
346 for (
size_t i = 1; i < argc; ++i) {
347 string currentArg(argv[i]);
349 if (i == 1 && (currentArg ==
"--help" || currentArg ==
"-h")) {
353 if (i == 1 && (currentArg ==
"--version" || currentArg ==
"-v")) {
354 printVersionAndExit();
357 if (currentArg.substr(0, 2) ==
"--") {
359 if (idAndVal.size() == 2) {
361 string const& value = idAndVal.at(1);
362 if (categoryAndKey.size() == 2) {
363 string const& category = categoryAndKey.at(0);
364 string const& key = categoryAndKey.at(1);
365 overrides.push_back({{category, key}, value});
374 if (currentArg !=
"--no-config-file") {
375 configPath = currentArg;
380 NLOG_WARNING(logger,
"WARNING: Invalid command line argument \"" << currentArg <<
"\" will be ignored")
385 if (!configPath && !noConfigFile) {
386 configPath =
"config.ini";
389 return {configPath, overrides};
393 setUpSignalHandlers();
401 setTerminationTimeout(*config);
402 setUpLogging(*config);
405 optional<PrivilegeDowngradeData> privilegeDowngradeData;
416 shared_ptr<HandleRequestFunctionWrapper> appHandleRequest;
429 requestHandlerPtr = RequestHandler::newRequestHandler(appHandleRequest, *config, concurrency);
437 if (privilegeDowngradeData) {
438 doPrivilegeDowngrade(*privilegeDowngradeData);
443 AppInit appInitStruct(*config, concurrency);
444 auto initReturn = appInit(appInitStruct);
447 if (initReturn != 0) {
448 NLOG_ERROR(logger,
"Fatal Error: App init function returned " << initReturn <<
" -- exiting.")
453 requestHandlerPtr->reconfigure(nullopt, appInitStruct.
accessFilters(), appInitStruct.
config());
457 requestHandlerPtr->start();
458 readyToReconfigure =
true;
464 requestHandlerPtr->join();
467 requestHandlerPtr.reset(
nullptr);
Reader for config files and accessor to config values.
Exception class that can be used by apps to catch errors resulting from nawa function calls.
Simple class for (not (yet) thread-safe) logging to stderr or to any other output stream.
#define NLOG_WARNING(Logger, Message)
#define NLOG_INFO(Logger, Message)
#define NLOG_DEBUG(Logger, Message)
#define NLOG_ERROR(Logger, Message)
Handles and serves incoming requests via the NAWA app.
This file will be configured by CMake and contains the necessary properties to ensure that a loaded a...
const int nawa_version_major
const int nawa_version_minor
AccessFilterList & accessFilters()
bool isSet(std::pair< std::string, std::string > const &key) const
virtual std::string getMessage() const noexcept
virtual std::string getDebugMessage() const noexcept
int * getGIDPtrForGetgrouplist(gid_t *in)
std::vector< std::string > splitString(std::string str, char delimiter, bool ignoreEmpty=false)
std::vector< ConfigOverride > configOverrides
Parameters parseCommandLine(int argc, char **argv)
std::optional< std::string > configFile
int(nawa::Connection &) handleRequest_t
int(nawa::AppInit &) init_t
unsigned int getConcurrency(nawa::Config const &config)
int run(Parameters const ¶meters)
std::optional< PrivilegeDowngradeData > preparePrivilegeDowngrade(nawa::Config const &config)
std::pair< init_t *, std::shared_ptr< nawa::HandleRequestFunctionWrapper > > loadAppFunctions(nawa::Config const &config)
void replaceLogger(nawa::Log const &log)
std::tuple< uid_t, gid_t, std::vector< gid_t > > PrivilegeDowngradeData
Definitions for the nawarun implementation.
This file contains helpers for operating-system specific stuff.
Contains useful functions that improve the readability and facilitate maintenance of the NAWA code.