--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/storage/GlobalStore.cc Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2004-2006 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <dtn-config.h>
+#endif
+
+#include <oasys/storage/DurableStore.h>
+#include <oasys/storage/StorageConfig.h>
+#include <oasys/serialize/TypeShims.h>
+#include <oasys/thread/Mutex.h>
+#include <oasys/util/MD5.h>
+
+#include "GlobalStore.h"
+#include "bundling/Bundle.h"
+#include "reg/APIRegistration.h"
+#include "routing/ProphetNode.h"
+
+namespace dtn {
+
+//----------------------------------------------------------------------
+const u_int32_t GlobalStore::CURRENT_VERSION = 3;
+static const char* GLOBAL_TABLE = "globals";
+static const char* GLOBAL_KEY = "global_key";
+
+//----------------------------------------------------------------------
+class Globals : public oasys::SerializableObject
+{
+public:
+ Globals() {}
+ Globals(const oasys::Builder&) {}
+
+ u_int32_t version_; ///< on-disk copy of CURRENT_VERSION
+ u_int32_t next_bundleid_; ///< running serial number for bundles
+ u_int32_t next_regid_; ///< running serial number for registrations
+ u_char digest_[oasys::MD5::MD5LEN]; ///< MD5 digest of all serialized fields
+
+ /**
+ * Virtual from SerializableObject.
+ */
+ virtual void serialize(oasys::SerializeAction* a);
+};
+
+//----------------------------------------------------------------------
+void
+Globals::serialize(oasys::SerializeAction* a)
+{
+ a->process("version", &version_);
+ a->process("next_bundleid", &next_bundleid_);
+ a->process("next_regid", &next_regid_);
+ a->process("digest", digest_, 16);
+}
+
+//----------------------------------------------------------------------
+GlobalStore* GlobalStore::instance_;
+
+//----------------------------------------------------------------------
+GlobalStore::GlobalStore()
+ : Logger("GlobalStore", "/dtn/storage/%s", GLOBAL_TABLE),
+ globals_(NULL), store_(NULL)
+{
+ lock_ = new oasys::Mutex(logpath_,
+ oasys::Mutex::TYPE_RECURSIVE,
+ true /* quiet */);
+}
+
+//----------------------------------------------------------------------
+int
+GlobalStore::init(const oasys::StorageConfig& cfg,
+ oasys::DurableStore* store)
+{
+ if (instance_ != NULL)
+ {
+ PANIC("GlobalStore::init called multiple times");
+ }
+
+ instance_ = new GlobalStore();
+ return instance_->do_init(cfg, store);
+}
+
+//----------------------------------------------------------------------
+int
+GlobalStore::do_init(const oasys::StorageConfig& cfg,
+ oasys::DurableStore* store)
+{
+ int flags = 0;
+
+ if (cfg.init_) {
+ flags |= oasys::DS_CREATE;
+ }
+
+ int err = store->get_table(&store_, GLOBAL_TABLE, flags);
+
+ if (err != 0) {
+ log_err("error initializing global store: %s",
+ (err == oasys::DS_NOTFOUND) ?
+ "table not found" :
+ "unknown error");
+ return err;
+ }
+
+ // if we're initializing the database for the first time, then we
+ // prime the values accordingly and sync the database version
+ if (cfg.init_)
+ {
+ log_info("initializing global table");
+
+ globals_ = new Globals();
+
+ globals_->version_ = CURRENT_VERSION;
+ globals_->next_bundleid_ = 0;
+ globals_->next_regid_ = Registration::MAX_RESERVED_REGID + 1;
+ calc_digest(globals_->digest_);
+
+ // store the new value
+ err = store_->put(oasys::StringShim(GLOBAL_KEY), globals_,
+ oasys::DS_CREATE | oasys::DS_EXCL);
+
+ if (err == oasys::DS_EXISTS)
+ {
+ // YUCK
+ log_err_p("/dtnd", "Initializing datastore which already exists.");
+ exit(1);
+ } else if (err != 0) {
+ log_err_p("/dtnd", "unknown error initializing global store");
+ return err;
+ }
+
+ loaded_ = true;
+
+ } else {
+ loaded_ = false;
+ }
+
+ return 0;
+}
+
+//----------------------------------------------------------------------
+GlobalStore::~GlobalStore()
+{
+ delete store_;
+ delete globals_;
+ delete lock_;
+}
+
+//----------------------------------------------------------------------
+u_int32_t
+GlobalStore::next_bundleid()
+{
+ oasys::ScopeLock l(lock_, "GlobalStore::next_bundleid");
+
+ ASSERT(globals_->next_bundleid_ != 0xffffffff);
+ log_debug("next_bundleid %d -> %d",
+ globals_->next_bundleid_,
+ globals_->next_bundleid_ + 1);
+
+ u_int32_t ret = globals_->next_bundleid_++;
+
+ update();
+
+ return ret;
+}
+
+//----------------------------------------------------------------------
+u_int32_t
+GlobalStore::next_regid()
+{
+ oasys::ScopeLock l(lock_, "GlobalStore::next_regid");
+
+ ASSERT(globals_->next_regid_ != 0xffffffff);
+ log_debug("next_regid %d -> %d",
+ globals_->next_regid_,
+ globals_->next_regid_ + 1);
+
+ u_int32_t ret = globals_->next_regid_++;
+
+ update();
+
+ return ret;
+}
+
+//----------------------------------------------------------------------
+void
+GlobalStore::calc_digest(u_char* digest)
+{
+ // We create dummy objects for all serialized objects, then take
+ // their serialized form and MD5 it, so adding or deleting a
+ // serialized field will change the digest
+ Bundle b(oasys::Builder::builder());
+ APIRegistration r(oasys::Builder::builder());
+ ProphetNode n(oasys::Builder::builder());
+
+ oasys::StringSerialize s(oasys::Serialize::CONTEXT_LOCAL,
+ oasys::StringSerialize::INCLUDE_NAME |
+ oasys::StringSerialize::INCLUDE_TYPE |
+ oasys::StringSerialize::SCHEMA_ONLY);
+
+ s.action(&b);
+ s.action(&r);
+ s.action(&n);
+
+ oasys::MD5 md5;
+ md5.update(s.buf().data(), s.buf().length());
+ md5.finalize();
+
+ log_debug("calculated digest %s for serialize string '%s'",
+ md5.digest_ascii().c_str(), s.buf().c_str());
+
+ memcpy(digest, md5.digest(), oasys::MD5::MD5LEN);
+}
+
+//----------------------------------------------------------------------
+bool
+GlobalStore::load()
+{
+ log_debug("loading global store");
+
+ oasys::StringShim key(GLOBAL_KEY);
+
+ if (globals_ != NULL) {
+ delete globals_;
+ globals_ = NULL;
+ }
+
+ if (store_->get(key, &globals_) != 0) {
+ log_crit("error loading global data");
+ return false;
+ }
+ ASSERT(globals_ != NULL);
+
+ if (globals_->version_ != CURRENT_VERSION) {
+ log_crit("datastore version mismatch: "
+ "expected version %d, database version %d",
+ CURRENT_VERSION, globals_->version_);
+ return false;
+ }
+
+ u_char digest[oasys::MD5::MD5LEN];
+ calc_digest(digest);
+
+ if (memcmp(digest, globals_->digest_, oasys::MD5::MD5LEN) != 0) {
+ log_crit("datastore digest mismatch: "
+ "expected %s, database contains %s",
+ oasys::hex2str(digest, oasys::MD5::MD5LEN).c_str(),
+ oasys::hex2str(globals_->digest_, oasys::MD5::MD5LEN).c_str());
+ log_crit("(implies serialized schema change)");
+ return false;
+ }
+
+ loaded_ = true;
+ return true;
+}
+
+//----------------------------------------------------------------------
+void
+GlobalStore::update()
+{
+ ASSERT(lock_->is_locked_by_me());
+
+ log_debug("updating global store");
+
+ // make certain we don't attempt to write out globals before
+ // load() has had a chance to load them from the database
+ ASSERT(loaded_);
+
+ int err = store_->put(oasys::StringShim(GLOBAL_KEY), globals_, 0);
+
+ if (err != 0) {
+ PANIC("GlobalStore::update fatal error updating database: %s",
+ oasys::durable_strerror(err));
+ }
+}
+
+//----------------------------------------------------------------------
+void
+GlobalStore::close()
+{
+ // we prevent the potential for shutdown race crashes by leaving
+ // the global store locked after it's been closed so other threads
+ // will simply block, not crash due to a null store
+ lock_->lock("GlobalStore::close");
+
+ delete store_;
+ store_ = NULL;
+
+ delete instance_;
+ instance_ = NULL;
+}
+
+} // namespace dtn