return output;
}
+String SHA1(const String& s, bool binary)
+{
+ char errbuf[120];
+ SHA_CTX context;
+ unsigned char digest[SHA_DIGEST_LENGTH];
+
+ if (!SHA1_Init(&context)) {
+ Log(LogCritical, "SSL")
+ << "Error on SHA Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+ BOOST_THROW_EXCEPTION(openssl_error()
+ << boost::errinfo_api_function("SHA1_Init")
+ << errinfo_openssl_error(ERR_peek_error()));
+ }
+
+ if (!SHA1_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
+ Log(LogCritical, "SSL")
+ << "Error on SHA Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+ BOOST_THROW_EXCEPTION(openssl_error()
+ << boost::errinfo_api_function("SHA1_Update")
+ << errinfo_openssl_error(ERR_peek_error()));
+ }
+
+ if (!SHA1_Final(digest, &context)) {
+ Log(LogCritical, "SSL")
+ << "Error on SHA Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+ BOOST_THROW_EXCEPTION(openssl_error()
+ << boost::errinfo_api_function("SHA1_Final")
+ << errinfo_openssl_error(ERR_peek_error()));
+ }
+
+ if (binary)
+ return String(reinterpret_cast<const char*>(digest));
+
+ char output[SHA_DIGEST_LENGTH*2+1];
+ for (int i = 0; i < 20; i++)
+ sprintf(output + 2 * i, "%02x", digest[i]);
+
+ return output;
+}
+
String SHA256(const String& s)
{
char errbuf[120];
String I2_BASE_API CertificateToString(const boost::shared_ptr<X509>& cert);
boost::shared_ptr<X509> I2_BASE_API CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject);
String I2_BASE_API PBKDF2_SHA1(const String& password, const String& salt, int iterations);
-String I2_BASE_API SHA1(const String& s);
+String I2_BASE_API SHA1(const String& s, bool binary = false);
String I2_BASE_API SHA256(const String& s);
String I2_BASE_API RandomString(int length);
mkclass_target(rediswriter.ti rediswriter.tcpp rediswriter.thpp)
set(redis_SOURCES
- rediswriter.cpp rediswriter-config.cpp rediswriter.thpp
+ rediswriter.cpp rediswriter-config.cpp rediswriter-utility.cpp rediswriter.thpp
)
if(ICINGA2_UNITY_BUILD)
#include "redis/rediswriter.hpp"
#include "icinga/customvarobject.hpp"
+#include "icinga/host.hpp"
+#include "icinga/service.hpp"
#include "base/json.hpp"
#include "base/logger.hpp"
#include "base/serializer.hpp"
+#include "base/tlsutility.hpp"
#include "base/initialize.hpp"
using namespace icinga;
ConfigObject::OnVersionChanged.connect(boost::bind(&RedisWriter::VersionChangedHandler, _1));
}
-//TODO: OnActiveChanged handling.
void RedisWriter::UpdateAllConfigObjects(void)
{
AssertOnWorkQueue();
- //TODO: Just use config types
for (const Type::Ptr& type : Type::GetAllTypes()) {
if (!ConfigObject::TypeInstance->IsAssignableFrom(type))
continue;
//TODO: checksum
String objectName = object->GetName();
- redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
+ redisReply *reply1 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
- if (!reply) {
+ if (!reply1) {
redisFree(m_Context);
m_Context = NULL;
return;
}
- if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
+ if (reply1->type == REDIS_REPLY_STATUS || reply1->type == REDIS_REPLY_ERROR) {
Log(LogInformation, "RedisWriter")
- << "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply->str;
+ << "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply1->str;
}
- if (reply->type == REDIS_REPLY_ERROR) {
- freeReplyObject(reply);
+ if (reply1->type == REDIS_REPLY_ERROR) {
+ freeReplyObject(reply1);
return;
}
- freeReplyObject(reply);
+ freeReplyObject(reply1);
+
+
+ /* check sums */
+ /* hset icinga:config:Host:checksums localhost { "name_checksum": "...", "properties_checksum": "...", "groups_checksum": "...", "vars_checksum": null } */
+ Dictionary::Ptr checkSum = new Dictionary();
+
+ checkSum->Set("name_checksum", CalculateCheckSumString(object->GetName()));
+
+ if (object->GetReflectionType() == Host::TypeInstance) {
+ Host::Ptr host = static_pointer_cast<Host>(object);
+ checkSum->Set("groups_checksum", CalculateCheckSumGroups(host->GetGroups()));
+ } else if (object->GetReflectionType() == Service::TypeInstance) {
+ Service::Ptr service = static_pointer_cast<Service>(object);
+ checkSum->Set("groups_checksum", CalculateCheckSumGroups(service->GetGroups()));
+ }
+
+ String checkSumBody = JsonEncode(checkSum);
+
+ redisReply *reply2 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s:checksum %s %s", typeName.CStr(), objectName.CStr(), checkSumBody.CStr()));
+
+ if (!reply2) {
+ redisFree(m_Context);
+ m_Context = NULL;
+ return;
+ }
+
+ if (reply2->type == REDIS_REPLY_STATUS || reply2->type == REDIS_REPLY_ERROR) {
+ Log(LogInformation, "RedisWriter")
+ << "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply2->str;
+ }
+
+ if (reply2->type == REDIS_REPLY_ERROR) {
+ freeReplyObject(reply2);
+ return;
+ }
+
+ freeReplyObject(reply2);
}
void RedisWriter::SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName)
freeReplyObject(reply);
}
-Dictionary::Ptr RedisWriter::SerializeObjectAttrs(const Object::Ptr& object, int fieldType)
-{
- Type::Ptr type = object->GetReflectionType();
-
- std::vector<int> fids;
-
- for (int fid = 0; fid < type->GetFieldCount(); fid++) {
- fids.push_back(fid);
- }
-
- Dictionary::Ptr resultAttrs = new Dictionary();
-
- for (int& fid : fids) {
- Field field = type->GetFieldInfo(fid);
-
- if ((field.Attributes & fieldType) == 0)
- continue;
-
- Value val = object->GetField(fid);
-
- /* hide attributes which shouldn't be user-visible */
- if (field.Attributes & FANoUserView)
- continue;
-
- /* hide internal navigation fields */
- if (field.Attributes & FANavigation && !(field.Attributes & (FAConfig | FAState)))
- continue;
-
- Value sval = Serialize(val);
- resultAttrs->Set(field.Name, sval);
- }
-
- return resultAttrs;
-}
-
void RedisWriter::StateChangedHandler(const ConfigObject::Ptr& object)
{
Type::Ptr type = object->GetReflectionType();
--- /dev/null
+/******************************************************************************
+ * Icinga 2 *
+ * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
+ * *
+ * 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 "redis/rediswriter.hpp"
+#include "icinga/customvarobject.hpp"
+#include "base/json.hpp"
+#include "base/logger.hpp"
+#include "base/serializer.hpp"
+#include "base/tlsutility.hpp"
+#include "base/initialize.hpp"
+
+using namespace icinga;
+
+String RedisWriter::FormatCheckSumBinary(const String& str)
+{
+ char output[20*2+1];
+ for (int i = 0; i < 20; i++)
+ sprintf(output + 2 * i, "%02x", str[i]);
+
+ return output;
+}
+
+String RedisWriter::CalculateCheckSumString(const String& str)
+{
+ return SHA1(str, true);
+}
+
+String RedisWriter::CalculateCheckSumGroups(const Array::Ptr& groups)
+{
+ String output;
+
+ bool first = true;
+
+ for (const String& group : groups) {
+ if (first)
+ first = false;
+ else
+ output += ";";
+
+ output += SHA1(group, true); //binary checksum required here
+ }
+
+ return SHA1(output, false);
+}
+
+String RedisWriter::CalculateCheckSumAttrs(const Dictionary::Ptr& attrs)
+{
+ String output;
+
+ //TODO: Implement
+ for (const Dictionary::Pair& kv: attrs) {
+ if (kv.second.IsNumber()) {
+ //use a precision of 6 for floating point numbers
+ }
+ }
+
+ return output;
+}
+
+Dictionary::Ptr RedisWriter::SerializeObjectAttrs(const Object::Ptr& object, int fieldType)
+{
+ Type::Ptr type = object->GetReflectionType();
+
+ std::vector<int> fids;
+
+ for (int fid = 0; fid < type->GetFieldCount(); fid++) {
+ fids.push_back(fid);
+ }
+
+ Dictionary::Ptr resultAttrs = new Dictionary();
+
+ for (int& fid : fids) {
+ Field field = type->GetFieldInfo(fid);
+
+ if ((field.Attributes & fieldType) == 0)
+ continue;
+
+ Value val = object->GetField(fid);
+
+ /* hide attributes which shouldn't be user-visible */
+ if (field.Attributes & FANoUserView)
+ continue;
+
+ /* hide internal navigation fields */
+ if (field.Attributes & FANavigation && !(field.Attributes & (FAConfig | FAState)))
+ continue;
+
+ Value sval = Serialize(val);
+ resultAttrs->Set(field.Name, sval);
+ }
+
+ return resultAttrs;
+}
+
void UpdateSubscriptionsTimerHandler(void);
void UpdateSubscriptions(void);
- /* config dump */
+ /* config & status dump */
void UpdateAllConfigObjects(void);
void SendConfigUpdate(const ConfigObject::Ptr& object, const String& typeName);
void SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName);
+
+ /* utilities */
+ static String FormatCheckSumBinary(const String& str);
+ static String CalculateCheckSumString(const String& str);
+ static String CalculateCheckSumGroups(const Array::Ptr& groups);
+ static String CalculateCheckSumAttrs(const Dictionary::Ptr& attrs);
static Dictionary::Ptr SerializeObjectAttrs(const Object::Ptr& object, int fieldType);
static void StateChangedHandler(const ConfigObject::Ptr& object);