mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-23 21:55:03 +02:00
parent
a68bfea737
commit
a4081f1445
6
debian/icinga2-common.icinga2.init
vendored
6
debian/icinga2-common.icinga2.init
vendored
@ -53,7 +53,7 @@ check_run () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_config () {
|
check_config () {
|
||||||
$DAEMON --validate -u "$DAEMON_USER" -g "$DAEMON_GROUP" -c "$DAEMON_CONFIG"
|
$DAEMON run --validate -u "$DAEMON_USER" -g "$DAEMON_GROUP" -c "$DAEMON_CONFIG"
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -69,7 +69,7 @@ do_start()
|
|||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
||||||
|| return 1
|
|| return 1
|
||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
|
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
|
||||||
-c "$DAEMON_CONFIG" -u "$DAEMON_USER" -g "$DAEMON_GROUP" -d $DAEMON_ARGS \
|
run -c "$DAEMON_CONFIG" -u "$DAEMON_USER" -g "$DAEMON_GROUP" -d $DAEMON_ARGS \
|
||||||
|| return 2
|
|| return 2
|
||||||
# Add code here, if necessary, that waits for the process to be ready
|
# Add code here, if necessary, that waits for the process to be ready
|
||||||
# to handle requests from services started subsequently which depend
|
# to handle requests from services started subsequently which depend
|
||||||
@ -84,7 +84,7 @@ do_foreground()
|
|||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test \
|
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test \
|
||||||
|| return 1
|
|| return 1
|
||||||
start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON -- \
|
start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON -- \
|
||||||
-c "$DAEMON_CONFIG" -u "$DAEMON_USER" -g "$DAEMON_GROUP" $DAEMON_ARGS \
|
run -c "$DAEMON_CONFIG" -u "$DAEMON_USER" -g "$DAEMON_GROUP" $DAEMON_ARGS \
|
||||||
|| return 2
|
|| return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
debian/icinga2-common.install
vendored
1
debian/icinga2-common.install
vendored
@ -1,6 +1,7 @@
|
|||||||
debian/config/apt.conf etc/icinga2/conf.d/hosts/localhost
|
debian/config/apt.conf etc/icinga2/conf.d/hosts/localhost
|
||||||
debian/tmp/etc/icinga2
|
debian/tmp/etc/icinga2
|
||||||
debian/tmp/etc/logrotate.d
|
debian/tmp/etc/logrotate.d
|
||||||
|
debian/tmp/etc/bash_completion.d
|
||||||
tools/syntax/* usr/share/icinga2-common/syntax
|
tools/syntax/* usr/share/icinga2-common/syntax
|
||||||
usr/bin/icinga2-build*
|
usr/bin/icinga2-build*
|
||||||
usr/bin/icinga2-sign-key
|
usr/bin/icinga2-sign-key
|
||||||
|
@ -68,6 +68,11 @@ if(NOT WIN32)
|
|||||||
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/checker.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/checker.conf\")")
|
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/checker.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/checker.conf\")")
|
||||||
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/notification.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/notification.conf\")")
|
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/notification.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/notification.conf\")")
|
||||||
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/mainlog.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/mainlog.conf\")")
|
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/mainlog.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/mainlog.conf\")")
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES bash_completion.d/icinga2
|
||||||
|
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/bash_completion.d
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
install(
|
install(
|
||||||
FILES icinga2/features-enabled/checker.conf icinga2/features-enabled/notification.conf
|
FILES icinga2/features-enabled/checker.conf icinga2/features-enabled/notification.conf
|
||||||
|
11
etc/bash_completion.d/icinga2
Normal file
11
etc/bash_completion.d/icinga2
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
_icinga2()
|
||||||
|
{
|
||||||
|
local cur opts
|
||||||
|
opts="${COMP_WORDS[*]}"
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
COMPREPLY=($(icinga2 --autocomplete ${COMP_WORDS[*]:1} < /dev/null))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _icinga2 icinga2
|
||||||
|
|
@ -48,7 +48,7 @@ start() {
|
|||||||
printf "Starting Icinga 2: "
|
printf "Starting Icinga 2: "
|
||||||
@CMAKE_INSTALL_FULL_SBINDIR@/icinga2-prepare-dirs $SYSCONFIGFILE
|
@CMAKE_INSTALL_FULL_SBINDIR@/icinga2-prepare-dirs $SYSCONFIGFILE
|
||||||
|
|
||||||
if ! $DAEMON -c $ICINGA2_CONFIG_FILE -d -e $ICINGA2_ERROR_LOG -u $ICINGA2_USER -g $ICINGA2_GROUP > $ICINGA2_STARTUP_LOG 2>&1; then
|
if ! $DAEMON daemon -c $ICINGA2_CONFIG_FILE -d -e $ICINGA2_ERROR_LOG -u $ICINGA2_USER -g $ICINGA2_GROUP > $ICINGA2_STARTUP_LOG 2>&1; then
|
||||||
echo "Error starting Icinga. Check '$ICINGA2_STARTUP_LOG' for details."
|
echo "Error starting Icinga. Check '$ICINGA2_STARTUP_LOG' for details."
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
@ -111,7 +111,7 @@ reload() {
|
|||||||
checkconfig() {
|
checkconfig() {
|
||||||
printf "Checking configuration: "
|
printf "Checking configuration: "
|
||||||
|
|
||||||
if ! $DAEMON -c $ICINGA2_CONFIG_FILE -C -u $ICINGA2_USER -g $ICINGA2_GROUP > $ICINGA2_STARTUP_LOG 2>&1; then
|
if ! $DAEMON daemon -c $ICINGA2_CONFIG_FILE -C -u $ICINGA2_USER -g $ICINGA2_GROUP > $ICINGA2_STARTUP_LOG 2>&1; then
|
||||||
if [ "x$1" = "x" ]; then
|
if [ "x$1" = "x" ]; then
|
||||||
cat $ICINGA2_STARTUP_LOG
|
cat $ICINGA2_STARTUP_LOG
|
||||||
echo "Icinga 2 detected configuration errors. Check '$ICINGA2_STARTUP_LOG' for details."
|
echo "Icinga 2 detected configuration errors. Check '$ICINGA2_STARTUP_LOG' for details."
|
||||||
|
@ -6,7 +6,7 @@ After=syslog.target postgresql.service mariadb.service carbon-cache.service
|
|||||||
Type=forking
|
Type=forking
|
||||||
EnvironmentFile=@ICINGA2_SYSCONFIGFILE@
|
EnvironmentFile=@ICINGA2_SYSCONFIGFILE@
|
||||||
ExecStartPre=@CMAKE_INSTALL_FULL_SBINDIR@/icinga2-prepare-dirs @ICINGA2_SYSCONFIGFILE@
|
ExecStartPre=@CMAKE_INSTALL_FULL_SBINDIR@/icinga2-prepare-dirs @ICINGA2_SYSCONFIGFILE@
|
||||||
ExecStart=@CMAKE_INSTALL_FULL_SBINDIR@/icinga2 -c ${ICINGA2_CONFIG_FILE} -d -e ${ICINGA2_ERROR_LOG} -u ${ICINGA2_USER} -g ${ICINGA2_GROUP}
|
ExecStart=@CMAKE_INSTALL_FULL_SBINDIR@/icinga2 daemon -c ${ICINGA2_CONFIG_FILE} -d -e ${ICINGA2_ERROR_LOG} -u ${ICINGA2_USER} -g ${ICINGA2_GROUP}
|
||||||
PIDFile=@ICINGA2_RUNDIR@/icinga2/icinga2.pid
|
PIDFile=@ICINGA2_RUNDIR@/icinga2/icinga2.pid
|
||||||
ExecReload=/bin/kill -HUP $MAINPID
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
|
||||||
|
@ -28,249 +28,20 @@
|
|||||||
#include "base/convert.hpp"
|
#include "base/convert.hpp"
|
||||||
#include "base/scriptvariable.hpp"
|
#include "base/scriptvariable.hpp"
|
||||||
#include "base/context.hpp"
|
#include "base/context.hpp"
|
||||||
|
#include "base/clicommand.hpp"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/tuple/tuple.hpp>
|
#include <boost/tuple/tuple.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
# include <sys/types.h>
|
|
||||||
# include <pwd.h>
|
|
||||||
# include <grp.h>
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
static po::variables_map g_AppParams;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SERVICE_STATUS l_SvcStatus;
|
SERVICE_STATUS l_SvcStatus;
|
||||||
SERVICE_STATUS_HANDLE l_SvcStatusHandle;
|
SERVICE_STATUS_HANDLE l_SvcStatusHandle;
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
static String LoadAppType(const String& typeSpec)
|
|
||||||
{
|
|
||||||
Log(LogInformation, "icinga-app", "Loading application type: " + typeSpec);
|
|
||||||
|
|
||||||
String::SizeType index = typeSpec.FindFirstOf('/');
|
|
||||||
|
|
||||||
if (index == String::NPos)
|
|
||||||
return typeSpec;
|
|
||||||
|
|
||||||
String library = typeSpec.SubStr(0, index);
|
|
||||||
|
|
||||||
(void) Utility::LoadExtensionLibrary(library);
|
|
||||||
|
|
||||||
return typeSpec.SubStr(index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void IncludeZoneDirRecursive(const String& path)
|
|
||||||
{
|
|
||||||
String zoneName = Utility::BaseName(path);
|
|
||||||
Utility::GlobRecursive(path, "*.conf", boost::bind(&ConfigCompiler::CompileFile, _1, zoneName), GlobFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void IncludeNonLocalZone(const String& zonePath)
|
|
||||||
{
|
|
||||||
String etcPath = Application::GetZonesDir() + "/" + Utility::BaseName(zonePath);
|
|
||||||
|
|
||||||
if (Utility::PathExists(etcPath))
|
|
||||||
return;
|
|
||||||
|
|
||||||
IncludeZoneDirRecursive(zonePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool LoadConfigFiles(const String& appType, const String& objectsFile = String())
|
|
||||||
{
|
|
||||||
ConfigCompilerContext::GetInstance()->Reset();
|
|
||||||
|
|
||||||
if (g_AppParams.count("config") > 0) {
|
|
||||||
BOOST_FOREACH(const String& configPath, g_AppParams["config"].as<std::vector<std::string> >()) {
|
|
||||||
ConfigCompiler::CompileFile(configPath);
|
|
||||||
}
|
|
||||||
} else if (!g_AppParams.count("no-config"))
|
|
||||||
ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
|
|
||||||
|
|
||||||
/* Load cluster config files - this should probably be in libremote but
|
|
||||||
* unfortunately moving it there is somewhat non-trivial. */
|
|
||||||
String zonesEtcDir = Application::GetZonesDir();
|
|
||||||
if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir))
|
|
||||||
Utility::Glob(zonesEtcDir + "/*", &IncludeZoneDirRecursive, GlobDirectory);
|
|
||||||
|
|
||||||
String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
|
|
||||||
if (Utility::PathExists(zonesVarDir))
|
|
||||||
Utility::Glob(zonesVarDir + "/*", &IncludeNonLocalZone, GlobDirectory);
|
|
||||||
|
|
||||||
String name, fragment;
|
|
||||||
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
|
|
||||||
ConfigCompiler::CompileText(name, fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>();
|
|
||||||
builder->SetType(appType);
|
|
||||||
builder->SetName("application");
|
|
||||||
ConfigItem::Ptr item = builder->Compile();
|
|
||||||
item->Register();
|
|
||||||
|
|
||||||
bool result = ConfigItem::ValidateItems(objectsFile);
|
|
||||||
|
|
||||||
int warnings = 0, errors = 0;
|
|
||||||
|
|
||||||
BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
|
|
||||||
std::ostringstream locbuf;
|
|
||||||
ShowCodeFragment(locbuf, message.Location, true);
|
|
||||||
String location = locbuf.str();
|
|
||||||
|
|
||||||
String logmsg;
|
|
||||||
|
|
||||||
if (!location.IsEmpty())
|
|
||||||
logmsg = "Location:\n" + location;
|
|
||||||
|
|
||||||
logmsg += String("\nConfig ") + (message.Error ? "error" : "warning") + ": " + message.Text;
|
|
||||||
|
|
||||||
if (message.Error) {
|
|
||||||
Log(LogCritical, "config", logmsg);
|
|
||||||
errors++;
|
|
||||||
} else {
|
|
||||||
Log(LogWarning, "config", logmsg);
|
|
||||||
warnings++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (warnings > 0 || errors > 0) {
|
|
||||||
LogSeverity severity;
|
|
||||||
|
|
||||||
if (errors == 0)
|
|
||||||
severity = LogWarning;
|
|
||||||
else
|
|
||||||
severity = LogCritical;
|
|
||||||
|
|
||||||
Log(severity, "config", Convert::ToString(errors) + " errors, " + Convert::ToString(warnings) + " warnings.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
static void SigHupHandler(int)
|
|
||||||
{
|
|
||||||
Application::RequestRestart();
|
|
||||||
}
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
static bool Daemonize(void)
|
|
||||||
{
|
|
||||||
#ifndef _WIN32
|
|
||||||
pid_t pid = fork();
|
|
||||||
if (pid == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid) {
|
|
||||||
// systemd requires that the pidfile of the daemon is written before the forking
|
|
||||||
// process terminates. So wait till either the forked daemon has written a pidfile or died.
|
|
||||||
|
|
||||||
int status;
|
|
||||||
int ret;
|
|
||||||
pid_t readpid;
|
|
||||||
do {
|
|
||||||
Utility::Sleep(0.1);
|
|
||||||
|
|
||||||
readpid = Application::ReadPidFile(Application::GetPidPath());
|
|
||||||
ret = waitpid(pid, &status, WNOHANG);
|
|
||||||
} while (readpid != pid && ret == 0);
|
|
||||||
|
|
||||||
if (ret == pid) {
|
|
||||||
Log(LogCritical, "icinga-app", "The daemon could not be started. See log output for details.");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
} else if (ret == -1) {
|
|
||||||
std::ostringstream msgbuf;
|
|
||||||
msgbuf << "waitpid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool SetDaemonIO(const String& stderrFile)
|
|
||||||
{
|
|
||||||
#ifndef _WIN32
|
|
||||||
int fdnull = open("/dev/null", O_RDWR);
|
|
||||||
if (fdnull >= 0) {
|
|
||||||
if (fdnull != 0)
|
|
||||||
dup2(fdnull, 0);
|
|
||||||
|
|
||||||
if (fdnull != 1)
|
|
||||||
dup2(fdnull, 1);
|
|
||||||
|
|
||||||
if (fdnull > 1)
|
|
||||||
close(fdnull);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *errPath = "/dev/null";
|
|
||||||
|
|
||||||
if (!stderrFile.IsEmpty())
|
|
||||||
errPath = stderrFile.CStr();
|
|
||||||
|
|
||||||
int fderr = open(errPath, O_WRONLY | O_APPEND);
|
|
||||||
|
|
||||||
if (fderr < 0 && errno == ENOENT)
|
|
||||||
fderr = open(errPath, O_CREAT | O_WRONLY | O_APPEND, 0600);
|
|
||||||
|
|
||||||
if (fderr > 0) {
|
|
||||||
if (fderr != 2)
|
|
||||||
dup2(fderr, 2);
|
|
||||||
|
|
||||||
if (fderr > 2)
|
|
||||||
close(fderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t sid = setsid();
|
|
||||||
if (sid == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Terminate another process and wait till it has ended
|
|
||||||
*
|
|
||||||
* @params target PID of the process to end
|
|
||||||
*/
|
|
||||||
static void TerminateAndWaitForEnd(pid_t target)
|
|
||||||
{
|
|
||||||
#ifndef _WIN32
|
|
||||||
// allow 30 seconds timeout
|
|
||||||
double timeout = Utility::GetTime() + 30;
|
|
||||||
|
|
||||||
int ret = kill(target, SIGTERM);
|
|
||||||
|
|
||||||
while (Utility::GetTime() < timeout && (ret == 0 || errno != ESRCH)) {
|
|
||||||
Utility::Sleep(0.1);
|
|
||||||
ret = kill(target, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// timeout and the process still seems to live: kill it
|
|
||||||
if (ret == 0 || errno != ESRCH)
|
|
||||||
kill(target, SIGKILL);
|
|
||||||
|
|
||||||
#else
|
|
||||||
// TODO: implement this for Win32
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
}
|
|
||||||
|
|
||||||
int Main(void)
|
int Main(void)
|
||||||
{
|
{
|
||||||
int argc = Application::GetArgC();
|
int argc = Application::GetArgC();
|
||||||
@ -329,46 +100,39 @@ int Main(void)
|
|||||||
Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d");
|
Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d");
|
||||||
Application::DeclareApplicationType("icinga/IcingaApplication");
|
Application::DeclareApplicationType("icinga/IcingaApplication");
|
||||||
|
|
||||||
po::options_description desc("Supported options");
|
LogSeverity logLevel = Logger::GetConsoleLogSeverity();
|
||||||
|
Logger::SetConsoleLogSeverity(LogWarning);
|
||||||
|
|
||||||
|
Utility::LoadExtensionLibrary("cli");
|
||||||
|
|
||||||
|
po::options_description desc("Global options");
|
||||||
|
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("help", "show this help message")
|
("help", "show this help message")
|
||||||
("version,V", "show version information")
|
("version,V", "show version information")
|
||||||
|
("define,D", po::value<std::vector<std::string> >(), "define a constant")
|
||||||
("library,l", po::value<std::vector<std::string> >(), "load a library")
|
("library,l", po::value<std::vector<std::string> >(), "load a library")
|
||||||
("include,I", po::value<std::vector<std::string> >(), "add include search directory")
|
("include,I", po::value<std::vector<std::string> >(), "add include search directory")
|
||||||
("define,D", po::value<std::vector<std::string> >(), "define a constant")
|
|
||||||
("config,c", po::value<std::vector<std::string> >(), "parse a configuration file")
|
|
||||||
("no-config,z", "start without a configuration file")
|
|
||||||
("validate,C", "exit after validating the configuration")
|
|
||||||
("log-level,x", po::value<std::string>(), "specify the log level for the console log")
|
("log-level,x", po::value<std::string>(), "specify the log level for the console log")
|
||||||
("errorlog,e", po::value<std::string>(), "log fatal errors to the specified log file (only works in combination with --daemonize)")
|
("no-stack-rlimit", "used internally, do not specify manually")
|
||||||
#ifndef _WIN32
|
("autocomplete", "auto-complete arguments");
|
||||||
("reload-internal", po::value<int>(), "used internally to implement config reload: do not call manually, send SIGHUP instead")
|
|
||||||
("daemonize,d", "detach from the controlling terminal")
|
String cmdname;
|
||||||
("user,u", po::value<std::string>(), "user to run Icinga as")
|
CLICommand::Ptr command;
|
||||||
("group,g", po::value<std::string>(), "group to run Icinga as")
|
bool autocomplete;
|
||||||
# ifdef RLIMIT_STACK
|
po::variables_map vm;
|
||||||
("no-stack-rlimit", "don't attempt to set RLIMIT_STACK")
|
|
||||||
# endif /* RLIMIT_STACK */
|
|
||||||
#else /* _WIN32 */
|
|
||||||
("scm", "run as a Windows service (must be the first argument if specified)")
|
|
||||||
("scm-install", "installs Icinga 2 as a Windows service (must be the first argument if specified")
|
|
||||||
("scm-uninstall", "uninstalls the Icinga 2 Windows service (must be the first argument if specified")
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
po::store(po::parse_command_line(argc, argv, desc), g_AppParams);
|
CLICommand::ParseCommand(argc, argv, desc, vm, cmdname, command, autocomplete);
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
std::ostringstream msgbuf;
|
std::ostringstream msgbuf;
|
||||||
msgbuf << "Error while parsing command-line options: " << ex.what();
|
msgbuf << "Error while parsing command-line options: " << ex.what();
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
Log(LogCritical, "cli_daemon", msgbuf.str());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
po::notify(g_AppParams);
|
if (vm.count("define")) {
|
||||||
|
BOOST_FOREACH(const String& define, vm["define"].as<std::vector<std::string> >()) {
|
||||||
if (g_AppParams.count("define")) {
|
|
||||||
BOOST_FOREACH(const String& define, g_AppParams["define"].as<std::vector<std::string> >()) {
|
|
||||||
String key, value;
|
String key, value;
|
||||||
size_t pos = define.FindFirstOf('=');
|
size_t pos = define.FindFirstOf('=');
|
||||||
if (pos != String::NPos) {
|
if (pos != String::NPos) {
|
||||||
@ -381,214 +145,109 @@ int Main(void)
|
|||||||
ScriptVariable::Set(key, value);
|
ScriptVariable::Set(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state");
|
Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state");
|
||||||
Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.debug");
|
Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.debug");
|
||||||
Application::DeclarePidPath(Application::GetRunDir() + "/icinga2/icinga2.pid");
|
Application::DeclarePidPath(Application::GetRunDir() + "/icinga2/icinga2.pid");
|
||||||
|
|
||||||
#ifndef _WIN32
|
ConfigCompiler::AddIncludeSearchDir(Application::GetIncludeConfDir());
|
||||||
if (g_AppParams.count("group")) {
|
|
||||||
String group = g_AppParams["group"].as<std::string>();
|
|
||||||
|
|
||||||
errno = 0;
|
if (!autocomplete && vm.count("include")) {
|
||||||
struct group *gr = getgrnam(group.CStr());
|
BOOST_FOREACH(const String& includePath, vm["include"].as<std::vector<std::string> >()) {
|
||||||
|
ConfigCompiler::AddIncludeSearchDir(includePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::SetConsoleLogSeverity(logLevel);
|
||||||
|
|
||||||
if (!gr) {
|
if (!autocomplete) {
|
||||||
if (errno == 0) {
|
if (vm.count("log-level")) {
|
||||||
std::ostringstream msgbuf;
|
String severity = vm["log-level"].as<std::string>();
|
||||||
msgbuf << "Invalid group specified: " + group;
|
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
LogSeverity logLevel = LogInformation;
|
||||||
return EXIT_FAILURE;
|
try {
|
||||||
} else {
|
logLevel = Logger::StringToSeverity(severity);
|
||||||
std::ostringstream msgbuf;
|
} catch (std::exception&) {
|
||||||
msgbuf << "getgrnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
/* use the default */
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
Log(LogWarning, "icinga", "Invalid log level set. Using default 'information'.");
|
||||||
return EXIT_FAILURE;
|
}
|
||||||
|
|
||||||
|
Logger::SetConsoleLogSeverity(logLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("library")) {
|
||||||
|
BOOST_FOREACH(const String& libraryName, vm["library"].as<std::vector<std::string> >()) {
|
||||||
|
(void)Utility::LoadExtensionLibrary(libraryName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_AppParams.count("reload-internal") && setgroups(0, NULL) < 0) {
|
if (!command || vm.count("help") || vm.count("version")) {
|
||||||
std::ostringstream msgbuf;
|
String appName = Utility::BaseName(Application::GetArgV()[0]);
|
||||||
msgbuf << "setgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-")
|
||||||
return EXIT_FAILURE;
|
appName = appName.SubStr(3, appName.GetLength() - 3);
|
||||||
}
|
|
||||||
|
std::cout << appName << " " << "- The Icinga 2 network monitoring daemon.";
|
||||||
if (setgid(gr->gr_gid) < 0) {
|
|
||||||
std::ostringstream msgbuf;
|
if (!command || vm.count("help")) {
|
||||||
msgbuf << "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
std::cout << std::endl << std::endl
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
<< "Usage:" << std::endl
|
||||||
return EXIT_FAILURE;
|
<< " " << argv[0] << " ";
|
||||||
}
|
|
||||||
}
|
if (cmdname.IsEmpty())
|
||||||
|
std::cout << "<command>";
|
||||||
if (g_AppParams.count("user")) {
|
else
|
||||||
String user = g_AppParams["user"].as<std::string>();
|
std::cout << cmdname;
|
||||||
|
|
||||||
errno = 0;
|
std::cout << " [<arguments>]";
|
||||||
struct passwd *pw = getpwnam(user.CStr());
|
|
||||||
|
if (command) {
|
||||||
if (!pw) {
|
std::cout << std::endl << std::endl
|
||||||
if (errno == 0) {
|
<< command->GetDescription();
|
||||||
std::ostringstream msgbuf;
|
}
|
||||||
msgbuf << "Invalid user specified: " + user;
|
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else {
|
|
||||||
std::ostringstream msgbuf;
|
|
||||||
msgbuf << "getpwnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (vm.count("version")) {
|
||||||
// also activate the additional groups the configured user is member of
|
std::cout << " (Version: " << Application::GetVersion() << ")";
|
||||||
if (!g_AppParams.count("reload-internal") && initgroups(user.CStr(), pw->pw_gid) < 0) {
|
std::cout << std::endl
|
||||||
std::ostringstream msgbuf;
|
<< "Copyright (c) 2012-2014 Icinga Development Team (http://www.icinga.org)" << std::endl
|
||||||
msgbuf << "initgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
<< "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl2.html>" << std::endl
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
<< "This is free software: you are free to change and redistribute it." << std::endl
|
||||||
return EXIT_FAILURE;
|
<< "There is NO WARRANTY, to the extent permitted by law.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setuid(pw->pw_uid) < 0) {
|
|
||||||
std::ostringstream msgbuf;
|
|
||||||
msgbuf << "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
|
||||||
Log(LogCritical, "icinga-app", msgbuf.str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
if (g_AppParams.count("log-level")) {
|
|
||||||
String severity = g_AppParams["log-level"].as<std::string>();
|
|
||||||
|
|
||||||
LogSeverity logLevel = LogInformation;
|
|
||||||
try {
|
|
||||||
logLevel = Logger::StringToSeverity(severity);
|
|
||||||
} catch (std::exception&) {
|
|
||||||
/* use the default */
|
|
||||||
Log(LogWarning, "icinga", "Invalid log level set. Using default 'information'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::SetConsoleLogSeverity(logLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_AppParams.count("help") || g_AppParams.count("version")) {
|
|
||||||
String appName = Utility::BaseName(argv[0]);
|
|
||||||
|
|
||||||
if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-")
|
|
||||||
appName = appName.SubStr(3, appName.GetLength() - 3);
|
|
||||||
|
|
||||||
std::cout << appName << " " << "- The Icinga 2 network monitoring daemon.";
|
|
||||||
|
|
||||||
if (g_AppParams.count("version")) {
|
|
||||||
std::cout << " (Version: " << Application::GetVersion() << ")";
|
|
||||||
std::cout << std::endl
|
|
||||||
<< "Copyright (c) 2012-2014 Icinga Development Team (http://www.icinga.org)" << std::endl
|
|
||||||
<< "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl2.html>" << std::endl
|
|
||||||
<< "This is free software: you are free to change and redistribute it." << std::endl
|
|
||||||
<< "There is NO WARRANTY, to the extent permitted by law.";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
if (g_AppParams.count("version")) {
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
Application::DisplayInfoMessage(true);
|
if (vm.count("version")) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
Application::DisplayInfoMessage(true);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!command || vm.count("help")) {
|
||||||
|
if (!command) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
CLICommand::ShowCommands(argc, argv, NULL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl
|
||||||
|
<< desc << std::endl
|
||||||
|
<< "Report bugs at <https://dev.icinga.org/>" << std::endl
|
||||||
|
<< "Icinga home page: <http://www.icinga.org/>" << std::endl;
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_AppParams.count("help")) {
|
int rc = 1;
|
||||||
std::cout << std::endl
|
|
||||||
<< desc << std::endl
|
|
||||||
<< "Report bugs at <https://dev.icinga.org/>" << std::endl
|
|
||||||
<< "Icinga home page: <http://www.icinga.org/>" << std::endl;
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptVariable::Set("UseVfork", true, false, true);
|
if (autocomplete) {
|
||||||
|
CLICommand::ShowCommands(argc, argv, &desc, true);
|
||||||
Application::MakeVariablesConstant();
|
rc = 0;
|
||||||
|
} else if (command)
|
||||||
Log(LogInformation, "icinga-app", "Icinga application loader (version: " + Application::GetVersion() + ")");
|
rc = command->Run(vm);
|
||||||
|
|
||||||
String appType = LoadAppType(Application::GetApplicationType());
|
|
||||||
|
|
||||||
if (g_AppParams.count("library")) {
|
|
||||||
BOOST_FOREACH(const String& libraryName, g_AppParams["library"].as<std::vector<std::string> >()) {
|
|
||||||
(void)Utility::LoadExtensionLibrary(libraryName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigCompiler::AddIncludeSearchDir(Application::GetIncludeConfDir());
|
|
||||||
|
|
||||||
if (g_AppParams.count("include")) {
|
|
||||||
BOOST_FOREACH(const String& includePath, g_AppParams["include"].as<std::vector<std::string> >()) {
|
|
||||||
ConfigCompiler::AddIncludeSearchDir(includePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!g_AppParams.count("validate") && !g_AppParams.count("reload-internal")) {
|
|
||||||
pid_t runningpid = Application::ReadPidFile(Application::GetPidPath());
|
|
||||||
if (runningpid > 0) {
|
|
||||||
Log(LogCritical, "icinga-app", "Another instance of Icinga already running with PID " + Convert::ToString(runningpid));
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LoadConfigFiles(appType, Application::GetObjectsPath()))
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
if (g_AppParams.count("validate")) {
|
|
||||||
Log(LogInformation, "icinga-app", "Finished validating the configuration file(s).");
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(g_AppParams.count("reload-internal")) {
|
|
||||||
int parentpid = g_AppParams["reload-internal"].as<int>();
|
|
||||||
Log(LogInformation, "icinga-app", "Terminating previous instance of Icinga (PID " + Convert::ToString(parentpid) + ")");
|
|
||||||
TerminateAndWaitForEnd(parentpid);
|
|
||||||
Log(LogInformation, "icinga-app", "Previous instance has ended, taking over now.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_AppParams.count("daemonize")) {
|
|
||||||
if (!g_AppParams.count("reload-internal")) {
|
|
||||||
// no additional fork neccessary on reload
|
|
||||||
try {
|
|
||||||
Daemonize();
|
|
||||||
} catch (std::exception&) {
|
|
||||||
Log(LogCritical, "icinga-app", "Daemonize failed. Exiting.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// activate config only after daemonization: it starts threads and that is not compatible with fork()
|
|
||||||
if (!ConfigItem::ActivateItems()) {
|
|
||||||
Log(LogCritical, "icinga-app", "Error activating configuration.");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_AppParams.count("daemonize")) {
|
|
||||||
String errorLog;
|
|
||||||
if (g_AppParams.count("errorlog"))
|
|
||||||
errorLog = g_AppParams["errorlog"].as<std::string>();
|
|
||||||
|
|
||||||
SetDaemonIO(errorLog);
|
|
||||||
Logger::DisableConsoleLog();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
struct sigaction sa;
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
|
||||||
sa.sa_handler = &SigHupHandler;
|
|
||||||
sigaction(SIGHUP, &sa, NULL);
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
int rc = Application::GetInstance()->Run();
|
|
||||||
|
|
||||||
#ifndef _DEBUG
|
#ifndef _DEBUG
|
||||||
Application::Exit(rc);
|
Application::Exit(rc);
|
||||||
|
@ -506,6 +506,7 @@ exit 0
|
|||||||
%doc COPYING COPYING.Exceptions README.md NEWS AUTHORS ChangeLog tools/syntax
|
%doc COPYING COPYING.Exceptions README.md NEWS AUTHORS ChangeLog tools/syntax
|
||||||
%attr(0750,%{icinga_user},%{icingacmd_group}) %dir %{_localstatedir}/log/%{name}
|
%attr(0750,%{icinga_user},%{icingacmd_group}) %dir %{_localstatedir}/log/%{name}
|
||||||
%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
|
%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
|
||||||
|
%{_sysconfdir}/bash_completion.d/%{name}
|
||||||
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_localstatedir}/spool/%{name}
|
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_localstatedir}/spool/%{name}
|
||||||
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_localstatedir}/spool/%{name}/perfdata
|
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_localstatedir}/spool/%{name}/perfdata
|
||||||
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_localstatedir}/spool/%{name}/tmp
|
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_localstatedir}/spool/%{name}/tmp
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
add_subdirectory(base)
|
add_subdirectory(base)
|
||||||
|
add_subdirectory(cli)
|
||||||
add_subdirectory(config)
|
add_subdirectory(config)
|
||||||
add_subdirectory(icinga)
|
add_subdirectory(icinga)
|
||||||
add_subdirectory(db_ido)
|
add_subdirectory(db_ido)
|
||||||
|
@ -23,7 +23,7 @@ mkclass_target(streamlogger.ti streamlogger.thpp)
|
|||||||
mkclass_target(sysloglogger.ti sysloglogger.thpp)
|
mkclass_target(sysloglogger.ti sysloglogger.thpp)
|
||||||
|
|
||||||
set(base_SOURCES
|
set(base_SOURCES
|
||||||
application.cpp application.thpp array.cpp configerror.cpp context.cpp
|
application.cpp application.thpp array.cpp clicommand.cpp configerror.cpp context.cpp
|
||||||
convert.cpp debuginfo.cpp dictionary.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp
|
convert.cpp debuginfo.cpp dictionary.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp
|
||||||
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp logger.cpp logger.thpp
|
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp logger.cpp logger.thpp
|
||||||
netstring.cpp networkstream.cpp object.cpp objectlock.cpp process.cpp
|
netstring.cpp networkstream.cpp object.cpp objectlock.cpp process.cpp
|
||||||
|
@ -135,8 +135,10 @@ void Application::InitializeBase(void)
|
|||||||
maxfds = 65536;
|
maxfds = 65536;
|
||||||
|
|
||||||
for (rlim_t i = 3; i < maxfds; i++) {
|
for (rlim_t i = 3; i < maxfds; i++) {
|
||||||
|
#ifdef _DEBUG
|
||||||
if (close(i) >= 0)
|
if (close(i) >= 0)
|
||||||
std::cerr << "Closed FD " << i << " which we inherited from our parent process." << std::endl;
|
std::cerr << "Closed FD " << i << " which we inherited from our parent process." << std::endl;
|
||||||
|
#endif /* _DEBUG */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
211
lib/base/clicommand.cpp
Normal file
211
lib/base/clicommand.cpp
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Icinga 2 *
|
||||||
|
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software Foundation *
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "base/clicommand.hpp"
|
||||||
|
#include "base/logger_fwd.hpp"
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace icinga;
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
boost::mutex l_RegistryMutex;
|
||||||
|
std::map<std::vector<String>, CLICommand::Ptr> l_Registry;
|
||||||
|
|
||||||
|
CLICommand::Ptr CLICommand::GetByName(const std::vector<String>& name)
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
||||||
|
|
||||||
|
std::map<std::vector<String>, CLICommand::Ptr>::const_iterator it = l_Registry.find(name);
|
||||||
|
|
||||||
|
if (it == l_Registry.end())
|
||||||
|
return CLICommand::Ptr();
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLICommand::Register(const std::vector<String>& name, const CLICommand::Ptr& function)
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
||||||
|
l_Registry[name] = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLICommand::Unregister(const std::vector<String>& name)
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
||||||
|
l_Registry.erase(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterCLICommandHelper::RegisterCLICommandHelper(const String& name, const CLICommand::Ptr& command)
|
||||||
|
{
|
||||||
|
std::vector<String> vname;
|
||||||
|
boost::algorithm::split(vname, name, boost::is_any_of("/"));
|
||||||
|
CLICommand::Register(vname, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& desc, po::variables_map& vm,
|
||||||
|
String& cmdname, CLICommand::Ptr& command, bool& autocomplete)
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
||||||
|
|
||||||
|
typedef std::map<std::vector<String>, CLICommand::Ptr>::value_type CLIKeyValue;
|
||||||
|
|
||||||
|
std::vector<String> best_match;
|
||||||
|
int arg_end = 1;
|
||||||
|
|
||||||
|
BOOST_FOREACH(const CLIKeyValue& kv, l_Registry) {
|
||||||
|
const std::vector<String>& vname = kv.first;
|
||||||
|
|
||||||
|
for (int i = 0, k = 1; i < vname.size() && k < argc; i++, k++) {
|
||||||
|
if (strcmp(argv[k], "--no-stack-rlimit") == 0 || strcmp(argv[k], "--autocomplete") == 0) {
|
||||||
|
if (strcmp(argv[k], "--autocomplete") == 0) {
|
||||||
|
autocomplete = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vname[i] != argv[k])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i >= best_match.size())
|
||||||
|
best_match.push_back(vname[i]);
|
||||||
|
|
||||||
|
if (i == vname.size() - 1) {
|
||||||
|
cmdname = boost::algorithm::join(vname, " ");
|
||||||
|
command = kv.second;
|
||||||
|
arg_end = k;
|
||||||
|
goto found_command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found_command:
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
po::options_description ldesc("Command options");
|
||||||
|
|
||||||
|
if (command)
|
||||||
|
command->InitParameters(ldesc);
|
||||||
|
|
||||||
|
desc.add(ldesc);
|
||||||
|
|
||||||
|
po::store(po::parse_command_line(argc - arg_end, argv + arg_end, desc), vm);
|
||||||
|
po::notify(vm);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLICommand::ShowCommands(int argc, char **argv, po::options_description *desc, bool autocomplete)
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
||||||
|
|
||||||
|
typedef std::map<std::vector<String>, CLICommand::Ptr>::value_type CLIKeyValue;
|
||||||
|
|
||||||
|
std::vector<String> best_match;
|
||||||
|
int arg_begin = 0;
|
||||||
|
CLICommand::Ptr command;
|
||||||
|
|
||||||
|
BOOST_FOREACH(const CLIKeyValue& kv, l_Registry) {
|
||||||
|
const std::vector<String>& vname = kv.first;
|
||||||
|
|
||||||
|
arg_begin = 0;
|
||||||
|
|
||||||
|
for (int i = 0, k = 1; i < vname.size() && k < argc; i++, k++) {
|
||||||
|
if (strcmp(argv[k], "--no-stack-rlimit") == 0 || strcmp(argv[k], "--autocomplete") == 0) {
|
||||||
|
i--;
|
||||||
|
arg_begin++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vname[i] != argv[k])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i >= best_match.size()) {
|
||||||
|
best_match.push_back(vname[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == vname.size() - 1) {
|
||||||
|
command = kv.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!autocomplete)
|
||||||
|
std::cout << "Supported commands: " << std::endl;
|
||||||
|
|
||||||
|
BOOST_FOREACH(const CLIKeyValue& kv, l_Registry) {
|
||||||
|
const std::vector<String>& vname = kv.first;
|
||||||
|
|
||||||
|
if (vname.size() < best_match.size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool match = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < best_match.size(); i++) {
|
||||||
|
if (vname[i] != best_match[i]) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (autocomplete) {
|
||||||
|
if (best_match.size() < vname.size()) {
|
||||||
|
String cname = vname[best_match.size()];
|
||||||
|
String pname;
|
||||||
|
|
||||||
|
if (arg_begin + best_match.size() + 1 < argc)
|
||||||
|
pname = argv[arg_begin + best_match.size() + 1];
|
||||||
|
|
||||||
|
if (cname.Find(pname) == 0)
|
||||||
|
std::cout << vname[best_match.size()] << " ";
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
std::cout << " * " << boost::algorithm::join(vname, " ") << " (" << kv.second->GetShortDescription() << ")" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command && autocomplete) {
|
||||||
|
po::options_description ldesc("Command options");
|
||||||
|
|
||||||
|
if (command)
|
||||||
|
command->InitParameters(ldesc);
|
||||||
|
|
||||||
|
desc->add(ldesc);
|
||||||
|
|
||||||
|
BOOST_FOREACH(const shared_ptr<po::option_description>& odesc, desc->options()) {
|
||||||
|
String cname = "--" + odesc->long_name();
|
||||||
|
String pname = argv[argc - 1];
|
||||||
|
|
||||||
|
if (cname.Find(pname) == 0)
|
||||||
|
std::cout << cname << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
74
lib/base/clicommand.hpp
Normal file
74
lib/base/clicommand.hpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Icinga 2 *
|
||||||
|
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software Foundation *
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CLICOMMAND_H
|
||||||
|
#define CLICOMMAND_H
|
||||||
|
|
||||||
|
#include "base/i2-base.hpp"
|
||||||
|
#include "base/value.hpp"
|
||||||
|
#include "base/utility.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
|
namespace icinga
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CLI command.
|
||||||
|
*
|
||||||
|
* @ingroup base
|
||||||
|
*/
|
||||||
|
class I2_BASE_API CLICommand : public Object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DECLARE_PTR_TYPEDEFS(CLICommand);
|
||||||
|
|
||||||
|
virtual String GetDescription(void) const = 0;
|
||||||
|
virtual String GetShortDescription(void) const = 0;
|
||||||
|
virtual void InitParameters(boost::program_options::options_description& desc) const = 0;
|
||||||
|
virtual int Run(const boost::program_options::variables_map& vm) const = 0;
|
||||||
|
|
||||||
|
static CLICommand::Ptr GetByName(const std::vector<String>& name);
|
||||||
|
static void Register(const std::vector<String>& name, const CLICommand::Ptr& command);
|
||||||
|
static void Unregister(const std::vector<String>& name);
|
||||||
|
|
||||||
|
static bool ParseCommand(int argc, char **argv, boost::program_options::options_description& desc,
|
||||||
|
boost::program_options::variables_map& vm, String& cmdname, CLICommand::Ptr& command, bool& autocomplete);
|
||||||
|
static void ShowCommands(int argc, char **argv, boost::program_options::options_description *desc, bool autocomplete);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for registering CLICommand implementation classes.
|
||||||
|
*
|
||||||
|
* @ingroup base
|
||||||
|
*/
|
||||||
|
class I2_BASE_API RegisterCLICommandHelper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RegisterCLICommandHelper(const String& name, const CLICommand::Ptr& command);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REGISTER_CLICOMMAND(name, klass) \
|
||||||
|
namespace { namespace UNIQUE_NAME(cli) { \
|
||||||
|
I2_EXPORT icinga::RegisterCLICommandHelper l_RegisterCLICommand(name, make_shared<klass>()); \
|
||||||
|
} }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CLICOMMAND_H */
|
@ -1001,7 +1001,7 @@ String Utility::GetFQDN(void)
|
|||||||
addrinfo *result;
|
addrinfo *result;
|
||||||
int rc = getaddrinfo(hostname.CStr(), NULL, &hints, &result);
|
int rc = getaddrinfo(hostname.CStr(), NULL, &hints, &result);
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc != 0)
|
||||||
result = NULL;
|
result = NULL;
|
||||||
|
|
||||||
String canonicalName;
|
String canonicalName;
|
||||||
|
42
lib/cli/CMakeLists.txt
Normal file
42
lib/cli/CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Icinga 2
|
||||||
|
# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation
|
||||||
|
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
set(cli_SOURCES
|
||||||
|
cainitcommand.cpp daemoncommand.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
if(ICINGA2_UNITY_BUILD)
|
||||||
|
mkunity_target(cli cli_SOURCES)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(cli SHARED ${cli_SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(cli ${Boost_LIBRARIES} base config)
|
||||||
|
|
||||||
|
set_target_properties (
|
||||||
|
cli PROPERTIES
|
||||||
|
INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2
|
||||||
|
FOLDER Lib
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS cli
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
54
lib/cli/cainitcommand.cpp
Normal file
54
lib/cli/cainitcommand.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Icinga 2 *
|
||||||
|
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software Foundation *
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "cli/cainitcommand.hpp"
|
||||||
|
#include "base/logger_fwd.hpp"
|
||||||
|
#include "base/clicommand.hpp"
|
||||||
|
|
||||||
|
using namespace icinga;
|
||||||
|
|
||||||
|
REGISTER_CLICOMMAND("ca/init", CAInitCommand);
|
||||||
|
|
||||||
|
String CAInitCommand::GetDescription(void) const
|
||||||
|
{
|
||||||
|
return "Sets up a new Certificate Authority.";
|
||||||
|
}
|
||||||
|
|
||||||
|
String CAInitCommand::GetShortDescription(void) const
|
||||||
|
{
|
||||||
|
return "sets up a new CA";
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAInitCommand::InitParameters(boost::program_options::options_description& desc) const
|
||||||
|
{
|
||||||
|
/* Command doesn't support any parameters. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry point for the "ca init" CLI command.
|
||||||
|
*
|
||||||
|
* @returns An exit status.
|
||||||
|
*/
|
||||||
|
int CAInitCommand::Run(const boost::program_options::variables_map& vm) const
|
||||||
|
{
|
||||||
|
Log(LogNotice, "cli", "Test!");
|
||||||
|
Log(LogInformation, "cli", "Hello World!");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
48
lib/cli/cainitcommand.hpp
Normal file
48
lib/cli/cainitcommand.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Icinga 2 *
|
||||||
|
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software Foundation *
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CAINITCOMMAND_H
|
||||||
|
#define CAINITCOMMAND_H
|
||||||
|
|
||||||
|
#include "base/qstring.hpp"
|
||||||
|
#include "base/clicommand.hpp"
|
||||||
|
|
||||||
|
namespace icinga
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "ca init" command.
|
||||||
|
*
|
||||||
|
* @ingroup cli
|
||||||
|
*/
|
||||||
|
class CAInitCommand : public CLICommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DECLARE_PTR_TYPEDEFS(CAInitCommand);
|
||||||
|
|
||||||
|
virtual String GetDescription(void) const;
|
||||||
|
virtual String GetShortDescription(void) const;
|
||||||
|
virtual void InitParameters(boost::program_options::options_description& desc) const;
|
||||||
|
virtual int Run(const boost::program_options::variables_map& vm) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CAINITCOMMAND_H */
|
446
lib/cli/daemoncommand.cpp
Normal file
446
lib/cli/daemoncommand.cpp
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Icinga 2 *
|
||||||
|
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software Foundation *
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "cli/daemoncommand.hpp"
|
||||||
|
#include "config/configcompilercontext.hpp"
|
||||||
|
#include "config/configcompiler.hpp"
|
||||||
|
#include "config/configitembuilder.hpp"
|
||||||
|
#include "base/logger_fwd.hpp"
|
||||||
|
#include "base/clicommand.hpp"
|
||||||
|
#include "base/application.hpp"
|
||||||
|
#include "base/logger.hpp"
|
||||||
|
#include "base/timer.hpp"
|
||||||
|
#include "base/utility.hpp"
|
||||||
|
#include "base/exception.hpp"
|
||||||
|
#include "base/convert.hpp"
|
||||||
|
#include "base/scriptvariable.hpp"
|
||||||
|
#include "base/context.hpp"
|
||||||
|
#include "config.h"
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <pwd.h>
|
||||||
|
# include <grp.h>
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
using namespace icinga;
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
static po::variables_map g_AppParams;
|
||||||
|
|
||||||
|
REGISTER_CLICOMMAND("daemon", DaemonCommand);
|
||||||
|
|
||||||
|
static String LoadAppType(const String& typeSpec)
|
||||||
|
{
|
||||||
|
Log(LogInformation, "cli", "Loading application type: " + typeSpec);
|
||||||
|
|
||||||
|
String::SizeType index = typeSpec.FindFirstOf('/');
|
||||||
|
|
||||||
|
if (index == String::NPos)
|
||||||
|
return typeSpec;
|
||||||
|
|
||||||
|
String library = typeSpec.SubStr(0, index);
|
||||||
|
|
||||||
|
(void) Utility::LoadExtensionLibrary(library);
|
||||||
|
|
||||||
|
return typeSpec.SubStr(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IncludeZoneDirRecursive(const String& path)
|
||||||
|
{
|
||||||
|
String zoneName = Utility::BaseName(path);
|
||||||
|
Utility::GlobRecursive(path, "*.conf", boost::bind(&ConfigCompiler::CompileFile, _1, zoneName), GlobFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IncludeNonLocalZone(const String& zonePath)
|
||||||
|
{
|
||||||
|
String etcPath = Application::GetZonesDir() + "/" + Utility::BaseName(zonePath);
|
||||||
|
|
||||||
|
if (Utility::PathExists(etcPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
IncludeZoneDirRecursive(zonePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool LoadConfigFiles(const boost::program_options::variables_map& vm, const String& appType, const String& objectsFile = String())
|
||||||
|
{
|
||||||
|
ConfigCompilerContext::GetInstance()->Reset();
|
||||||
|
|
||||||
|
if (vm.count("config") > 0) {
|
||||||
|
BOOST_FOREACH(const String& configPath, vm["config"].as<std::vector<std::string> >()) {
|
||||||
|
ConfigCompiler::CompileFile(configPath);
|
||||||
|
}
|
||||||
|
} else if (!vm.count("no-config"))
|
||||||
|
ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
|
||||||
|
|
||||||
|
/* Load cluster config files - this should probably be in libremote but
|
||||||
|
* unfortunately moving it there is somewhat non-trivial. */
|
||||||
|
String zonesEtcDir = Application::GetZonesDir();
|
||||||
|
if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir))
|
||||||
|
Utility::Glob(zonesEtcDir + "/*", &IncludeZoneDirRecursive, GlobDirectory);
|
||||||
|
|
||||||
|
String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
|
||||||
|
if (Utility::PathExists(zonesVarDir))
|
||||||
|
Utility::Glob(zonesVarDir + "/*", &IncludeNonLocalZone, GlobDirectory);
|
||||||
|
|
||||||
|
String name, fragment;
|
||||||
|
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
|
||||||
|
ConfigCompiler::CompileText(name, fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>();
|
||||||
|
builder->SetType(appType);
|
||||||
|
builder->SetName("application");
|
||||||
|
ConfigItem::Ptr item = builder->Compile();
|
||||||
|
item->Register();
|
||||||
|
|
||||||
|
bool result = ConfigItem::ValidateItems(objectsFile);
|
||||||
|
|
||||||
|
int warnings = 0, errors = 0;
|
||||||
|
|
||||||
|
BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
|
||||||
|
std::ostringstream locbuf;
|
||||||
|
ShowCodeFragment(locbuf, message.Location, true);
|
||||||
|
String location = locbuf.str();
|
||||||
|
|
||||||
|
String logmsg;
|
||||||
|
|
||||||
|
if (!location.IsEmpty())
|
||||||
|
logmsg = "Location:\n" + location;
|
||||||
|
|
||||||
|
logmsg += String("\nConfig ") + (message.Error ? "error" : "warning") + ": " + message.Text;
|
||||||
|
|
||||||
|
if (message.Error) {
|
||||||
|
Log(LogCritical, "config", logmsg);
|
||||||
|
errors++;
|
||||||
|
} else {
|
||||||
|
Log(LogWarning, "config", logmsg);
|
||||||
|
warnings++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warnings > 0 || errors > 0) {
|
||||||
|
LogSeverity severity;
|
||||||
|
|
||||||
|
if (errors == 0)
|
||||||
|
severity = LogWarning;
|
||||||
|
else
|
||||||
|
severity = LogCritical;
|
||||||
|
|
||||||
|
Log(severity, "config", Convert::ToString(errors) + " errors, " + Convert::ToString(warnings) + " warnings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static void SigHupHandler(int)
|
||||||
|
{
|
||||||
|
Application::RequestRestart();
|
||||||
|
}
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
static bool Daemonize(void)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid) {
|
||||||
|
// systemd requires that the pidfile of the daemon is written before the forking
|
||||||
|
// process terminates. So wait till either the forked daemon has written a pidfile or died.
|
||||||
|
|
||||||
|
int status;
|
||||||
|
int ret;
|
||||||
|
pid_t readpid;
|
||||||
|
do {
|
||||||
|
Utility::Sleep(0.1);
|
||||||
|
|
||||||
|
readpid = Application::ReadPidFile(Application::GetPidPath());
|
||||||
|
ret = waitpid(pid, &status, WNOHANG);
|
||||||
|
} while (readpid != pid && ret == 0);
|
||||||
|
|
||||||
|
if (ret == pid) {
|
||||||
|
Log(LogCritical, "cli", "The daemon could not be started. See log output for details.");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else if (ret == -1) {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "waitpid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SetDaemonIO(const String& stderrFile)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
int fdnull = open("/dev/null", O_RDWR);
|
||||||
|
if (fdnull >= 0) {
|
||||||
|
if (fdnull != 0)
|
||||||
|
dup2(fdnull, 0);
|
||||||
|
|
||||||
|
if (fdnull != 1)
|
||||||
|
dup2(fdnull, 1);
|
||||||
|
|
||||||
|
if (fdnull > 1)
|
||||||
|
close(fdnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *errPath = "/dev/null";
|
||||||
|
|
||||||
|
if (!stderrFile.IsEmpty())
|
||||||
|
errPath = stderrFile.CStr();
|
||||||
|
|
||||||
|
int fderr = open(errPath, O_WRONLY | O_APPEND);
|
||||||
|
|
||||||
|
if (fderr < 0 && errno == ENOENT)
|
||||||
|
fderr = open(errPath, O_CREAT | O_WRONLY | O_APPEND, 0600);
|
||||||
|
|
||||||
|
if (fderr > 0) {
|
||||||
|
if (fderr != 2)
|
||||||
|
dup2(fderr, 2);
|
||||||
|
|
||||||
|
if (fderr > 2)
|
||||||
|
close(fderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t sid = setsid();
|
||||||
|
if (sid == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminate another process and wait till it has ended
|
||||||
|
*
|
||||||
|
* @params target PID of the process to end
|
||||||
|
*/
|
||||||
|
static void TerminateAndWaitForEnd(pid_t target)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
// allow 30 seconds timeout
|
||||||
|
double timeout = Utility::GetTime() + 30;
|
||||||
|
|
||||||
|
int ret = kill(target, SIGTERM);
|
||||||
|
|
||||||
|
while (Utility::GetTime() < timeout && (ret == 0 || errno != ESRCH)) {
|
||||||
|
Utility::Sleep(0.1);
|
||||||
|
ret = kill(target, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout and the process still seems to live: kill it
|
||||||
|
if (ret == 0 || errno != ESRCH)
|
||||||
|
kill(target, SIGKILL);
|
||||||
|
|
||||||
|
#else
|
||||||
|
// TODO: implement this for Win32
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
}
|
||||||
|
|
||||||
|
String DaemonCommand::GetDescription(void) const
|
||||||
|
{
|
||||||
|
return "Starts Icinga 2.";
|
||||||
|
}
|
||||||
|
|
||||||
|
String DaemonCommand::GetShortDescription(void) const
|
||||||
|
{
|
||||||
|
return "starts Icinga 2";
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonCommand::InitParameters(boost::program_options::options_description& desc) const
|
||||||
|
{
|
||||||
|
desc.add_options()
|
||||||
|
("config,c", po::value<std::vector<std::string> >(), "parse a configuration file")
|
||||||
|
("no-config,z", "start without a configuration file")
|
||||||
|
("validate,C", "exit after validating the configuration")
|
||||||
|
("errorlog,e", po::value<std::string>(), "log fatal errors to the specified log file (only works in combination with --daemonize)")
|
||||||
|
#ifndef _WIN32
|
||||||
|
("reload-internal", po::value<int>(), "used internally to implement config reload: do not call manually, send SIGHUP instead")
|
||||||
|
("daemonize,d", "detach from the controlling terminal")
|
||||||
|
("user,u", po::value<std::string>(), "user to run Icinga as")
|
||||||
|
("group,g", po::value<std::string>(), "group to run Icinga as")
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry point for the "daemon" CLI command.
|
||||||
|
*
|
||||||
|
* @returns An exit status.
|
||||||
|
*/
|
||||||
|
int DaemonCommand::Run(const po::variables_map& vm) const
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
if (vm.count("group")) {
|
||||||
|
String group = vm["group"].as<std::string>();
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
struct group *gr = getgrnam(group.CStr());
|
||||||
|
|
||||||
|
if (!gr) {
|
||||||
|
if (errno == 0) {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "Invalid group specified: " + group;
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "getgrnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vm.count("reload-internal") && setgroups(0, NULL) < 0) {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "setgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setgid(gr->gr_gid) < 0) {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("user")) {
|
||||||
|
String user = vm["user"].as<std::string>();
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
struct passwd *pw = getpwnam(user.CStr());
|
||||||
|
|
||||||
|
if (!pw) {
|
||||||
|
if (errno == 0) {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "Invalid user specified: " + user;
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "getpwnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// also activate the additional groups the configured user is member of
|
||||||
|
if (!vm.count("reload-internal") && initgroups(user.CStr(), pw->pw_gid) < 0) {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "initgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setuid(pw->pw_uid) < 0) {
|
||||||
|
std::ostringstream msgbuf;
|
||||||
|
msgbuf << "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
||||||
|
Log(LogCritical, "cli", msgbuf.str());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
ScriptVariable::Set("UseVfork", true, false, true);
|
||||||
|
|
||||||
|
Application::MakeVariablesConstant();
|
||||||
|
|
||||||
|
Log(LogInformation, "cli", "Icinga application loader (version: " + Application::GetVersion() + ")");
|
||||||
|
|
||||||
|
String appType = LoadAppType(Application::GetApplicationType());
|
||||||
|
|
||||||
|
if (!vm.count("validate") && !vm.count("reload-internal")) {
|
||||||
|
pid_t runningpid = Application::ReadPidFile(Application::GetPidPath());
|
||||||
|
if (runningpid > 0) {
|
||||||
|
Log(LogCritical, "cli", "Another instance of Icinga already running with PID " + Convert::ToString(runningpid));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LoadConfigFiles(vm, appType, Application::GetObjectsPath()))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
if (vm.count("validate")) {
|
||||||
|
Log(LogInformation, "cli", "Finished validating the configuration file(s).");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vm.count("reload-internal")) {
|
||||||
|
int parentpid = vm["reload-internal"].as<int>();
|
||||||
|
Log(LogInformation, "cli", "Terminating previous instance of Icinga (PID " + Convert::ToString(parentpid) + ")");
|
||||||
|
TerminateAndWaitForEnd(parentpid);
|
||||||
|
Log(LogInformation, "cli", "Previous instance has ended, taking over now.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("daemonize")) {
|
||||||
|
if (!vm.count("reload-internal")) {
|
||||||
|
// no additional fork neccessary on reload
|
||||||
|
try {
|
||||||
|
Daemonize();
|
||||||
|
} catch (std::exception&) {
|
||||||
|
Log(LogCritical, "cli", "Daemonize failed. Exiting.");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// activate config only after daemonization: it starts threads and that is not compatible with fork()
|
||||||
|
if (!ConfigItem::ActivateItems()) {
|
||||||
|
Log(LogCritical, "cli", "Error activating configuration.");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("daemonize")) {
|
||||||
|
String errorLog;
|
||||||
|
if (vm.count("errorlog"))
|
||||||
|
errorLog = vm["errorlog"].as<std::string>();
|
||||||
|
|
||||||
|
SetDaemonIO(errorLog);
|
||||||
|
Logger::DisableConsoleLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
struct sigaction sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sa_handler = &SigHupHandler;
|
||||||
|
sigaction(SIGHUP, &sa, NULL);
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
return Application::GetInstance()->Run();
|
||||||
|
}
|
48
lib/cli/daemoncommand.hpp
Normal file
48
lib/cli/daemoncommand.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Icinga 2 *
|
||||||
|
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software Foundation *
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DAEMONCOMMAND_H
|
||||||
|
#define DAEMONCOMMAND_H
|
||||||
|
|
||||||
|
#include "base/clicommand.hpp"
|
||||||
|
#include "base/qstring.hpp"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace icinga
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "daemon" CLI command.
|
||||||
|
*
|
||||||
|
* @ingroup cli
|
||||||
|
*/
|
||||||
|
class DaemonCommand : public CLICommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DECLARE_PTR_TYPEDEFS(DaemonCommand);
|
||||||
|
|
||||||
|
virtual String GetDescription(void) const;
|
||||||
|
virtual String GetShortDescription(void) const;
|
||||||
|
virtual void InitParameters(boost::program_options::options_description& desc) const;
|
||||||
|
virtual int Run(const boost::program_options::variables_map& vm) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* DAEMONCOMMAND_H */
|
Loading…
x
Reference in New Issue
Block a user