--------------------|-------------------
PrefixDir |**Read-only.** Contains the installation prefix that was specified with cmake -DCMAKE_INSTALL_PREFIX. Defaults to "/usr/local".
SysconfDir |**Read-only.** Contains the path of the sysconf directory. Defaults to PrefixDir + "/etc".
+ZonesDir |**Read-only.** Contains the path of the zones.d directory. Defaults to SysconfDir + "/zones.d".
LocalStateDir |**Read-only.** Contains the path of the local state directory. Defaults to PrefixDir + "/var".
PkgDataDir |**Read-only.** Contains the path of the package data directory. Defaults to PrefixDir + "/share/icinga2".
StatePath |**Read-write.** Contains the path of the Icinga 2 state file. Defaults to LocalStateDir + "/lib/icinga2/icinga2.state".
install_if_not_exists(icinga2/scripts/check_kernel ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
install_if_not_exists(icinga2/scripts/mail-host-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
install_if_not_exists(icinga2/scripts/mail-service-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
+install_if_not_exists(icinga2/zones.d/README ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/zones.d)
install_if_not_exists(logrotate.d/icinga2 ${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d)
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/pki\")")
return typeSpec.SubStr(index + 1);
}
+static void IncludeDirRecursive(const String& path)
+{
+ Utility::GlobRecursive(path, "*.conf", &ConfigCompiler::CompileFile, GlobFile);
+}
+
+static void IncludeNonLocalZone(const String& zonePath)
+{
+ String etcPath = Application::GetZonesDir() + "/" + Utility::BaseName(zonePath);
+
+#ifndef _WIN32
+ struct stat statbuf;
+ if (lstat(etcPath.CStr(), &statbuf) >= 0)
+#else /* _WIN32 */
+ struct _stat statbuf;
+ if (_stat(etcPath.CStr(), &statbuf) >= 0)
+#endif /* _WIN32 */
+ return;
+
+ IncludeDirRecursive(zonePath);
+}
+
static bool LoadConfigFiles(const String& appType)
{
ConfigCompilerContext::GetInstance()->Reset();
}
}
+ IncludeDirRecursive(Application::GetZonesDir());
+ Utility::Glob(Application::GetLocalStateDir() + "/lib/icinga2/api/zones/*", &IncludeNonLocalZone, GlobDirectory);
+
+ /* Load cluster config files - this should probably be in libremote but
+ * unfortunately moving it there is somewhat non-trivial. */
+
String name, fragment;
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
ConfigCompiler::CompileText(name, fragment);
}
#endif /* _WIN32 */
+ Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d");
Application::DeclareApplicationType("icinga/IcingaApplication");
po::options_description desc("Supported options");
return ScriptVariable::Get("LocalStateDir");
}
+/**
+ * Sets the path of the zones dir.
+ *
+ * @param path The new path.
+ */
+void Application::DeclareZonesDir(const String& path)
+{
+ ScriptVariable::Set("ZonesDir", path, false);
+}
+
+/**
+ * Retrieves the path for the local state dir.
+ *
+ * @returns The path.
+ */
+String Application::GetZonesDir(void)
+{
+ return ScriptVariable::Get("ZonesDir");
+}
+
/**
* Sets the path for the local state dir.
*
static String GetSysconfDir(void);
static void DeclareSysconfDir(const String& path);
+ static String GetZonesDir(void);
+ static void DeclareZonesDir(const String& path);
+
static String GetLocalStateDir(void);
static void DeclareLocalStateDir(const String& path);
mkembedconfig_target(remote-type.conf remote-type.cpp)
add_library(remote SHARED
- apiclient.cpp apifunction.cpp apilistener.cpp apilistener.th authority.cpp
- endpoint.cpp endpoint.th jsonrpc.cpp messageorigin.cpp remote-type.cpp
- zone.cpp zone.th
+ apiclient.cpp apifunction.cpp apilistener.cpp apilistener-sync.cpp
+ apilistener.th authority.cpp endpoint.cpp endpoint.th jsonrpc.cpp
+ messageorigin.cpp remote-type.cpp zone.cpp zone.th
)
include_directories(${Boost_INCLUDE_DIRS})
#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")")
+install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/zones\")")
+
--- /dev/null
+/******************************************************************************
+ * 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 "remote/apilistener.h"
+#include "base/dynamictype.h"
+#include "base/logger_fwd.h"
+#include <boost/foreach.hpp>
+#include <fstream>
+
+using namespace icinga;
+
+bool ApiListener::IsConfigMaster(const Zone::Ptr& zone) const
+{
+ String path = Application::GetZonesDir() + "/" + zone->GetName();
+
+#ifndef _WIN32
+ struct stat statbuf;
+ return (lstat(path.CStr(), &statbuf) >= 0);
+#else /* _WIN32 */
+ struct _stat statbuf;
+ return (_stat(path.CStr(), &statbuf) >= 0);
+#endif /* _WIN32 */
+}
+
+void ApiListener::ConfigGlobHandler(const Dictionary::Ptr& config, const String& path, const String& file)
+{
+ CONTEXT("Creating config update for file '" + file + "'");
+
+ std::ifstream fp(file.CStr());
+ if (!fp)
+ return;
+
+ String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
+ config->Set(file.SubStr(path.GetLength()), content);
+}
+
+void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
+{
+ Log(LogInformation, "remote", "Syncing zone: " + zone->GetName());
+
+ String dirNew = Application::GetZonesDir() + "/" + zone->GetName();
+ String dirOld = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName();
+
+#ifndef _WIN32
+ if (mkdir(dirOld.CStr(), 0700) < 0 && errno != EEXIST) {
+#else /*_ WIN32 */
+ if (mkdir(dirOld.CStr()) < 0 && errno != EEXIST) {
+#endif /* _WIN32 */
+ BOOST_THROW_EXCEPTION(posix_error()
+ << boost::errinfo_api_function("mkdir")
+ << boost::errinfo_errno(errno)
+ << boost::errinfo_file_name(dirOld));
+ }
+
+ Dictionary::Ptr configNew = make_shared<Dictionary>();
+ Utility::GlobRecursive(dirNew, "*.conf", boost::bind(&ApiListener::ConfigGlobHandler, configNew, dirNew, _1), GlobFile);
+
+ Dictionary::Ptr configOld = make_shared<Dictionary>();
+ Utility::GlobRecursive(dirOld, "*.conf", boost::bind(&ApiListener::ConfigGlobHandler, configOld, dirOld, _1), GlobFile);
+
+ BOOST_FOREACH(const Dictionary::Pair& kv, configNew) {
+ if (configOld->Get(kv.first) != kv.second) {
+ String path = dirOld + "/" + kv.first;
+ Log(LogInformation, "remote", "Updating configuration file: " + path);
+
+ std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc);
+ fp << kv.second;
+ fp.close();
+ }
+ }
+
+ BOOST_FOREACH(const Dictionary::Pair& kv, configOld) {
+ if (!configNew->Contains(kv.first)) {
+ String path = dirOld + "/" + kv.first;
+ (void) unlink(path.CStr());
+ }
+ }
+}
+
+void ApiListener::SyncZoneDirs(void) const
+{
+ BOOST_FOREACH(const Zone::Ptr& zone, DynamicType::GetObjects<Zone>()) {
+ if (!IsConfigMaster(zone))
+ continue;
+
+ SyncZoneDir(zone);
+ }
+
+ bool configChange = false;
+
+ // TODO: remove configuration files for zones which don't exist anymore (i.e. don't have a Zone object)
+
+ if (configChange) {
+ Log(LogInformation, "remote", "Restarting after configuration change.");
+ Application::RequestRestart();
+ }
+}
if (!Endpoint::GetByName(GetIdentity()))
BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint object for '" + GetIdentity() + "' is missing."));
+
+ SyncZoneDirs();
}
/**
void CloseLogFile(void);
static void LogGlobHandler(std::vector<int>& files, const String& file);
void ReplayLog(const ApiClient::Ptr& client);
+
+ void SyncZoneDirs(void) const;
+ void SyncZoneDir(const Zone::Ptr& zone) const;
+ bool IsConfigMaster(const Zone::Ptr& zone) const;
+ static void ConfigGlobHandler(const Dictionary::Ptr& config, const String& path, const String& file);
+
};
}
#include "base/dynamicobject.h"
+#include "base/application.h"
namespace icinga
{
%attribute %string "crl_path",
%attribute %string "bind_host",
- %attribute %string "bind_port"
+ %attribute %string "bind_port",
}
%type Endpoint {