From c8b4fee8436d7de322f94d9d0bea77eda0945926 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 21 Jun 2017 11:36:14 +0200 Subject: [PATCH] Make rlimits configurable by adding three variables: RLimitFiles, RLimitProcesses and RLimitStack refs #5367 --- doc/17-language-reference.md | 15 ++- icinga-app/icinga.cpp | 49 +++++---- lib/base/application.cpp | 206 +++++++++++++++++++++++++++-------- lib/base/application.hpp | 12 ++ 4 files changed, 210 insertions(+), 72 deletions(-) diff --git a/doc/17-language-reference.md b/doc/17-language-reference.md index adc20520f..989d9ae48 100644 --- a/doc/17-language-reference.md +++ b/doc/17-language-reference.md @@ -368,6 +368,8 @@ Global constants can be set using the `const` keyword: Once defined a constant can be accessed from any file. Constants cannot be changed once they are set. +### Icinga 2 Specific Constants + Icinga 2 provides a number of special global constants. Some of them can be overridden using the `--define` command line parameter: Variable |Description @@ -383,8 +385,6 @@ ObjectsPath |**Read-write.** Contains the path of the Icinga 2 objects f PidPath |**Read-write.** Contains the path of the Icinga 2 PID file. Defaults to RunDir + "/icinga2/icinga2.pid". Vars |**Read-write.** Contains a dictionary with global custom attributes. Not set by default. NodeName |**Read-write.** Contains the cluster node name. Set to the local hostname by default. -EventEngine |**Read-write.** The name of the socket event engine, can be "poll" or "epoll". The epoll interface is only supported on Linux. -AttachDebugger |**Read-write.** Whether to attach a debugger when Icinga 2 crashes. Defaults to false. RunAsUser |**Read-write.** Defines the user the Icinga 2 daemon is running as. Used in the `init.conf` configuration file. RunAsGroup |**Read-write.** Defines the group the Icinga 2 daemon is running as. Used in the `init.conf` configuration file. PlatformName |**Read-only.** The name of the operating system, e.g. "Ubuntu". @@ -395,6 +395,17 @@ BuildCompilerName |**Read-only.** The name of the compiler Icinga was built wi BuildCompilerVersion|**Read-only.** The version of the compiler Icinga was built with, e.g. "7.3.0.7030031". BuildHostName |**Read-only.** The name of the host Icinga was built on, e.g. "acheron". + +Advanced runtime constants. Please only use them if advised by support or developers. + +Variable |Description +--------------------|------------------- +EventEngine |**Read-write.** The name of the socket event engine, can be `poll` or `epoll`. The epoll interface is only supported on Linux. +AttachDebugger |**Read-write.** Whether to attach a debugger when Icinga 2 crashes. Defaults to `false`. +RLimitFiles |**Read-write.** Defines the resource limit for RLIMIT_NOFILE that should be set at start-up. Value cannot be set lower than the default `16 * 1024`. 0 disables the setting. Used in the `init.conf` configuration file. +RLimitProcesses |**Read-write.** Defines the resource limit for RLIMIT_NPROC that should be set at start-up. Value cannot be set lower than the default `16 * 1024`. 0 disables the setting. Used in the `init.conf` configuration file. +RLimitStack |**Read-write.** Defines the resource limit for RLIMIT_STACK that should be set at start-up. Value cannot be set lower than the default `256 * 1024`. 0 disables the setting. Used in the `init.conf` configuration file. + ## Apply The `apply` keyword can be used to create new objects which are associated with diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index f264db96c..2007e8c58 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -113,9 +113,6 @@ int Main(void) Application::SetStartTime(Utility::GetTime()); - if (!autocomplete) - Application::SetResourceLimits(); - /* Set thread title. */ Utility::SetThreadName("Main Thread", false); @@ -152,6 +149,11 @@ int Main(void) Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d"); Application::DeclareRunAsUser(ICINGA_USER); Application::DeclareRunAsGroup(ICINGA_GROUP); +#ifdef __linux__ + Application::DeclareRLimitFiles(Application::GetDefaultRLimitFiles()); + Application::DeclareRLimitProcesses(Application::GetDefaultRLimitProcesses()); + Application::DeclareRLimitStack(Application::GetDefaultRLimitStack()); +#endif /* __linux__ */ Application::DeclareConcurrency(boost::thread::hardware_concurrency()); ScriptGlobal::Set("AttachDebugger", false); @@ -166,6 +168,28 @@ int Main(void) ScriptGlobal::Set("BuildCompilerName", ICINGA_BUILD_COMPILER_NAME); ScriptGlobal::Set("BuildCompilerVersion", ICINGA_BUILD_COMPILER_VERSION); + String initconfig = Application::GetSysconfDir() + "/icinga2/init.conf"; + + if (Utility::PathExists(initconfig)) { + Expression *expression; + try { + expression = ConfigCompiler::CompileFile(initconfig); + + ScriptFrame frame; + expression->Evaluate(frame); + } catch (const std::exception& ex) { + delete expression; + + Log(LogCritical, "config", DiagnosticInformation(ex)); + return EXIT_FAILURE; + } + + delete expression; + } + + if (!autocomplete) + Application::SetResourceLimits(); + LogSeverity logLevel = Logger::GetConsoleLogSeverity(); Logger::SetConsoleLogSeverity(LogWarning); @@ -209,25 +233,6 @@ int Main(void) return EXIT_FAILURE; } - String initconfig = Application::GetSysconfDir() + "/icinga2/init.conf"; - - if (Utility::PathExists(initconfig)) { - Expression *expression; - try { - expression = ConfigCompiler::CompileFile(initconfig); - - ScriptFrame frame; - expression->Evaluate(frame); - } catch (const std::exception& ex) { - delete expression; - - Log(LogCritical, "config", DiagnosticInformation(ex)); - return EXIT_FAILURE; - } - - delete expression; - } - #ifndef _WIN32 if (vm.count("color")) { Console::SetType(std::cout, Console_VT100); diff --git a/lib/base/application.cpp b/lib/base/application.cpp index b6bb02861..c1c2692c8 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -180,24 +180,44 @@ void Application::SetResourceLimits(void) rlimit rl; # ifdef RLIMIT_NOFILE - rl.rlim_cur = 16 * 1024; - rl.rlim_max = rl.rlim_cur; + rlim_t fileLimit = GetRLimitFiles(); - if (setrlimit(RLIMIT_NOFILE, &rl) < 0) - Log(LogNotice, "Application", "Could not adjust resource limit for open file handles (RLIMIT_NOFILE)"); + if (fileLimit != 0) { + if (fileLimit < GetDefaultRLimitFiles()) { + Log(LogWarning, "Application") + << "The user-specified value for RLimitFiles cannot be smaller than the default value (" << GetDefaultRLimitFiles() << "). Using the default value instead."; + fileLimit = GetDefaultRLimitFiles(); + } + + rl.rlim_cur = fileLimit; + rl.rlim_max = rl.rlim_cur; + + if (setrlimit(RLIMIT_NOFILE, &rl) < 0) + Log(LogNotice, "Application", "Could not adjust resource limit for open file handles (RLIMIT_NOFILE)"); # else /* RLIMIT_NOFILE */ - Log(LogNotice, "Application", "System does not support adjusting the resource limit for open file handles (RLIMIT_NOFILE)"); + Log(LogNotice, "Application", "System does not support adjusting the resource limit for open file handles (RLIMIT_NOFILE)"); # endif /* RLIMIT_NOFILE */ + } # ifdef RLIMIT_NPROC - rl.rlim_cur = 16 * 1024; - rl.rlim_max = rl.rlim_cur; + rlim_t processLimit = GetRLimitProcesses(); - if (setrlimit(RLIMIT_NPROC, &rl) < 0) - Log(LogNotice, "Application", "Could not adjust resource limit for number of processes (RLIMIT_NPROC)"); + if (processLimit != 0) { + if (processLimit < GetDefaultRLimitProcesses()) { + Log(LogWarning, "Application") + << "The user-specified value for RLimitProcesses cannot be smaller than the default value (" << GetDefaultRLimitProcesses() << "). Using the default value instead."; + processLimit = GetDefaultRLimitProcesses(); + } + + rl.rlim_cur = processLimit; + rl.rlim_max = rl.rlim_cur; + + if (setrlimit(RLIMIT_NPROC, &rl) < 0) + Log(LogNotice, "Application", "Could not adjust resource limit for number of processes (RLIMIT_NPROC)"); # else /* RLIMIT_NPROC */ - Log(LogNotice, "Application", "System does not support adjusting the resource limit for number of processes (RLIMIT_NPROC)"); + Log(LogNotice, "Application", "System does not support adjusting the resource limit for number of processes (RLIMIT_NPROC)"); # endif /* RLIMIT_NPROC */ + } # ifdef RLIMIT_STACK int argc = Application::GetArgC(); @@ -216,41 +236,53 @@ void Application::SetResourceLimits(void) rl.rlim_max = RLIM_INFINITY; } - if (set_stack_rlimit) - rl.rlim_cur = 256 * 1024; - else - rl.rlim_cur = rl.rlim_max; + rlim_t stackLimit; - if (setrlimit(RLIMIT_STACK, &rl) < 0) - Log(LogNotice, "Application", "Could not adjust resource limit for stack size (RLIMIT_STACK)"); - else if (set_stack_rlimit) { - char **new_argv = static_cast(malloc(sizeof(char *) * (argc + 2))); + stackLimit = GetRLimitStack(); - if (!new_argv) { - perror("malloc"); - Exit(EXIT_FAILURE); + if (stackLimit != 0) { + if (stackLimit < GetDefaultRLimitStack()) { + Log(LogWarning, "Application") + << "The user-specified value for RLimitStack cannot be smaller than the default value (" << GetDefaultRLimitStack() << "). Using the default value instead."; + stackLimit = GetDefaultRLimitStack(); } - new_argv[0] = argv[0]; - new_argv[1] = strdup("--no-stack-rlimit"); + if (set_stack_rlimit) + rl.rlim_cur = stackLimit; + else + rl.rlim_cur = rl.rlim_max; - if (!new_argv[1]) { - perror("strdup"); - exit(1); + if (setrlimit(RLIMIT_STACK, &rl) < 0) + Log(LogNotice, "Application", "Could not adjust resource limit for stack size (RLIMIT_STACK)"); + else if (set_stack_rlimit) { + char **new_argv = static_cast(malloc(sizeof(char *) * (argc + 2))); + + if (!new_argv) { + perror("malloc"); + Exit(EXIT_FAILURE); + } + + new_argv[0] = argv[0]; + new_argv[1] = strdup("--no-stack-rlimit"); + + if (!new_argv[1]) { + perror("strdup"); + exit(1); + } + + for (int i = 1; i < argc; i++) + new_argv[i + 1] = argv[i]; + + new_argv[argc + 1] = NULL; + + (void) execvp(new_argv[0], new_argv); + perror("execvp"); + _exit(EXIT_FAILURE); } - - for (int i = 1; i < argc; i++) - new_argv[i + 1] = argv[i]; - - new_argv[argc + 1] = NULL; - - (void) execvp(new_argv[0], new_argv); - perror("execvp"); - _exit(EXIT_FAILURE); - } # else /* RLIMIT_STACK */ - Log(LogNotice, "Application", "System does not support adjusting the resource limit for stack size (RLIMIT_STACK)"); + Log(LogNotice, "Application", "System does not support adjusting the resource limit for stack size (RLIMIT_STACK)"); # endif /* RLIMIT_STACK */ + } #endif /* __linux__ */ } @@ -1331,6 +1363,95 @@ String Application::GetRunAsGroup(void) return ScriptGlobal::Get("RunAsGroup"); } +/** + * Sets the name of the group. + * + * @param path The new group name. + */ +void Application::DeclareRunAsGroup(const String& group) +{ + if (!ScriptGlobal::Exists("RunAsGroup")) + ScriptGlobal::Set("RunAsGroup", group); +} + +/** + * Retrieves the file rlimit. + * + * @returns The limit. + */ +int Application::GetRLimitFiles(void) +{ + return ScriptGlobal::Get("RLimitFiles"); +} + +int Application::GetDefaultRLimitFiles(void) +{ + return 16 * 1024; +} + +/** + * Sets the file rlimit. + * + * @param path The new file rlimit. + */ +void Application::DeclareRLimitFiles(int limit) +{ + if (!ScriptGlobal::Exists("RLimitFiles")) + ScriptGlobal::Set("RLimitFiles", limit); +} + +/** + * Retrieves the process rlimit. + * + * @returns The limit. + */ +int Application::GetRLimitProcesses(void) +{ + return ScriptGlobal::Get("RLimitProcesses"); +} + +int Application::GetDefaultRLimitProcesses(void) +{ + return 16 * 1024; +} + +/** + * Sets the process rlimit. + * + * @param path The new process rlimit. + */ +void Application::DeclareRLimitProcesses(int limit) +{ + if (!ScriptGlobal::Exists("RLimitProcesses")) + ScriptGlobal::Set("RLimitProcesses", limit); +} + +/** + * Retrieves the stack rlimit. + * + * @returns The limit. + */ +int Application::GetRLimitStack(void) +{ + return ScriptGlobal::Get("RLimitStack"); +} + +int Application::GetDefaultRLimitStack(void) +{ + return 256 * 1024; +} + +/** + * Sets the stack rlimit. + * + * @param path The new stack rlimit. + */ +void Application::DeclareRLimitStack(int limit) +{ + if (!ScriptGlobal::Exists("RLimitStack")) + ScriptGlobal::Set("RLimitStack", limit); +} + /** * Sets the concurrency level. * @@ -1353,17 +1474,6 @@ int Application::GetConcurrency(void) return ScriptGlobal::Get("Concurrency", &defaultConcurrency); } -/** - * Sets the name of the group. - * - * @param path The new group name. - */ -void Application::DeclareRunAsGroup(const String& group) -{ - if (!ScriptGlobal::Exists("RunAsGroup")) - ScriptGlobal::Set("RunAsGroup", group); -} - /** * Returns the global thread pool. * diff --git a/lib/base/application.hpp b/lib/base/application.hpp index 4009a6843..5c1469e12 100644 --- a/lib/base/application.hpp +++ b/lib/base/application.hpp @@ -126,6 +126,18 @@ public: static String GetRunAsGroup(void); static void DeclareRunAsGroup(const String& group); + static int GetRLimitFiles(void); + static int GetDefaultRLimitFiles(void); + static void DeclareRLimitFiles(int limit); + + static int GetRLimitProcesses(void); + static int GetDefaultRLimitProcesses(void); + static void DeclareRLimitProcesses(int limit); + + static int GetRLimitStack(void); + static int GetDefaultRLimitStack(void); + static void DeclareRLimitStack(int limit); + static int GetConcurrency(void); static void DeclareConcurrency(int ncpus);