|
1 /* |
|
2 * Copyright 2004-2006 Intel Corporation |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #ifdef HAVE_CONFIG_H |
|
18 # include <dtn-config.h> |
|
19 #endif |
|
20 |
|
21 #include <oasys/storage/DurableStore.h> |
|
22 #include <oasys/storage/StorageConfig.h> |
|
23 #include <oasys/serialize/TypeShims.h> |
|
24 #include <oasys/thread/Mutex.h> |
|
25 #include <oasys/util/MD5.h> |
|
26 |
|
27 #include "GlobalStore.h" |
|
28 #include "bundling/Bundle.h" |
|
29 #include "reg/APIRegistration.h" |
|
30 #include "routing/ProphetNode.h" |
|
31 |
|
32 namespace dtn { |
|
33 |
|
34 //---------------------------------------------------------------------- |
|
35 const u_int32_t GlobalStore::CURRENT_VERSION = 3; |
|
36 static const char* GLOBAL_TABLE = "globals"; |
|
37 static const char* GLOBAL_KEY = "global_key"; |
|
38 |
|
39 //---------------------------------------------------------------------- |
|
40 class Globals : public oasys::SerializableObject |
|
41 { |
|
42 public: |
|
43 Globals() {} |
|
44 Globals(const oasys::Builder&) {} |
|
45 |
|
46 u_int32_t version_; ///< on-disk copy of CURRENT_VERSION |
|
47 u_int32_t next_bundleid_; ///< running serial number for bundles |
|
48 u_int32_t next_regid_; ///< running serial number for registrations |
|
49 u_char digest_[oasys::MD5::MD5LEN]; ///< MD5 digest of all serialized fields |
|
50 |
|
51 /** |
|
52 * Virtual from SerializableObject. |
|
53 */ |
|
54 virtual void serialize(oasys::SerializeAction* a); |
|
55 }; |
|
56 |
|
57 //---------------------------------------------------------------------- |
|
58 void |
|
59 Globals::serialize(oasys::SerializeAction* a) |
|
60 { |
|
61 a->process("version", &version_); |
|
62 a->process("next_bundleid", &next_bundleid_); |
|
63 a->process("next_regid", &next_regid_); |
|
64 a->process("digest", digest_, 16); |
|
65 } |
|
66 |
|
67 //---------------------------------------------------------------------- |
|
68 GlobalStore* GlobalStore::instance_; |
|
69 |
|
70 //---------------------------------------------------------------------- |
|
71 GlobalStore::GlobalStore() |
|
72 : Logger("GlobalStore", "/dtn/storage/%s", GLOBAL_TABLE), |
|
73 globals_(NULL), store_(NULL) |
|
74 { |
|
75 lock_ = new oasys::Mutex(logpath_, |
|
76 oasys::Mutex::TYPE_RECURSIVE, |
|
77 true /* quiet */); |
|
78 } |
|
79 |
|
80 //---------------------------------------------------------------------- |
|
81 int |
|
82 GlobalStore::init(const oasys::StorageConfig& cfg, |
|
83 oasys::DurableStore* store) |
|
84 { |
|
85 if (instance_ != NULL) |
|
86 { |
|
87 PANIC("GlobalStore::init called multiple times"); |
|
88 } |
|
89 |
|
90 instance_ = new GlobalStore(); |
|
91 return instance_->do_init(cfg, store); |
|
92 } |
|
93 |
|
94 //---------------------------------------------------------------------- |
|
95 int |
|
96 GlobalStore::do_init(const oasys::StorageConfig& cfg, |
|
97 oasys::DurableStore* store) |
|
98 { |
|
99 int flags = 0; |
|
100 |
|
101 if (cfg.init_) { |
|
102 flags |= oasys::DS_CREATE; |
|
103 } |
|
104 |
|
105 int err = store->get_table(&store_, GLOBAL_TABLE, flags); |
|
106 |
|
107 if (err != 0) { |
|
108 log_err("error initializing global store: %s", |
|
109 (err == oasys::DS_NOTFOUND) ? |
|
110 "table not found" : |
|
111 "unknown error"); |
|
112 return err; |
|
113 } |
|
114 |
|
115 // if we're initializing the database for the first time, then we |
|
116 // prime the values accordingly and sync the database version |
|
117 if (cfg.init_) |
|
118 { |
|
119 log_info("initializing global table"); |
|
120 |
|
121 globals_ = new Globals(); |
|
122 |
|
123 globals_->version_ = CURRENT_VERSION; |
|
124 globals_->next_bundleid_ = 0; |
|
125 globals_->next_regid_ = Registration::MAX_RESERVED_REGID + 1; |
|
126 calc_digest(globals_->digest_); |
|
127 |
|
128 // store the new value |
|
129 err = store_->put(oasys::StringShim(GLOBAL_KEY), globals_, |
|
130 oasys::DS_CREATE | oasys::DS_EXCL); |
|
131 |
|
132 if (err == oasys::DS_EXISTS) |
|
133 { |
|
134 // YUCK |
|
135 log_err_p("/dtnd", "Initializing datastore which already exists."); |
|
136 exit(1); |
|
137 } else if (err != 0) { |
|
138 log_err_p("/dtnd", "unknown error initializing global store"); |
|
139 return err; |
|
140 } |
|
141 |
|
142 loaded_ = true; |
|
143 |
|
144 } else { |
|
145 loaded_ = false; |
|
146 } |
|
147 |
|
148 return 0; |
|
149 } |
|
150 |
|
151 //---------------------------------------------------------------------- |
|
152 GlobalStore::~GlobalStore() |
|
153 { |
|
154 delete store_; |
|
155 delete globals_; |
|
156 delete lock_; |
|
157 } |
|
158 |
|
159 //---------------------------------------------------------------------- |
|
160 u_int32_t |
|
161 GlobalStore::next_bundleid() |
|
162 { |
|
163 oasys::ScopeLock l(lock_, "GlobalStore::next_bundleid"); |
|
164 |
|
165 ASSERT(globals_->next_bundleid_ != 0xffffffff); |
|
166 log_debug("next_bundleid %d -> %d", |
|
167 globals_->next_bundleid_, |
|
168 globals_->next_bundleid_ + 1); |
|
169 |
|
170 u_int32_t ret = globals_->next_bundleid_++; |
|
171 |
|
172 update(); |
|
173 |
|
174 return ret; |
|
175 } |
|
176 |
|
177 //---------------------------------------------------------------------- |
|
178 u_int32_t |
|
179 GlobalStore::next_regid() |
|
180 { |
|
181 oasys::ScopeLock l(lock_, "GlobalStore::next_regid"); |
|
182 |
|
183 ASSERT(globals_->next_regid_ != 0xffffffff); |
|
184 log_debug("next_regid %d -> %d", |
|
185 globals_->next_regid_, |
|
186 globals_->next_regid_ + 1); |
|
187 |
|
188 u_int32_t ret = globals_->next_regid_++; |
|
189 |
|
190 update(); |
|
191 |
|
192 return ret; |
|
193 } |
|
194 |
|
195 //---------------------------------------------------------------------- |
|
196 void |
|
197 GlobalStore::calc_digest(u_char* digest) |
|
198 { |
|
199 // We create dummy objects for all serialized objects, then take |
|
200 // their serialized form and MD5 it, so adding or deleting a |
|
201 // serialized field will change the digest |
|
202 Bundle b(oasys::Builder::builder()); |
|
203 APIRegistration r(oasys::Builder::builder()); |
|
204 ProphetNode n(oasys::Builder::builder()); |
|
205 |
|
206 oasys::StringSerialize s(oasys::Serialize::CONTEXT_LOCAL, |
|
207 oasys::StringSerialize::INCLUDE_NAME | |
|
208 oasys::StringSerialize::INCLUDE_TYPE | |
|
209 oasys::StringSerialize::SCHEMA_ONLY); |
|
210 |
|
211 s.action(&b); |
|
212 s.action(&r); |
|
213 s.action(&n); |
|
214 |
|
215 oasys::MD5 md5; |
|
216 md5.update(s.buf().data(), s.buf().length()); |
|
217 md5.finalize(); |
|
218 |
|
219 log_debug("calculated digest %s for serialize string '%s'", |
|
220 md5.digest_ascii().c_str(), s.buf().c_str()); |
|
221 |
|
222 memcpy(digest, md5.digest(), oasys::MD5::MD5LEN); |
|
223 } |
|
224 |
|
225 //---------------------------------------------------------------------- |
|
226 bool |
|
227 GlobalStore::load() |
|
228 { |
|
229 log_debug("loading global store"); |
|
230 |
|
231 oasys::StringShim key(GLOBAL_KEY); |
|
232 |
|
233 if (globals_ != NULL) { |
|
234 delete globals_; |
|
235 globals_ = NULL; |
|
236 } |
|
237 |
|
238 if (store_->get(key, &globals_) != 0) { |
|
239 log_crit("error loading global data"); |
|
240 return false; |
|
241 } |
|
242 ASSERT(globals_ != NULL); |
|
243 |
|
244 if (globals_->version_ != CURRENT_VERSION) { |
|
245 log_crit("datastore version mismatch: " |
|
246 "expected version %d, database version %d", |
|
247 CURRENT_VERSION, globals_->version_); |
|
248 return false; |
|
249 } |
|
250 |
|
251 u_char digest[oasys::MD5::MD5LEN]; |
|
252 calc_digest(digest); |
|
253 |
|
254 if (memcmp(digest, globals_->digest_, oasys::MD5::MD5LEN) != 0) { |
|
255 log_crit("datastore digest mismatch: " |
|
256 "expected %s, database contains %s", |
|
257 oasys::hex2str(digest, oasys::MD5::MD5LEN).c_str(), |
|
258 oasys::hex2str(globals_->digest_, oasys::MD5::MD5LEN).c_str()); |
|
259 log_crit("(implies serialized schema change)"); |
|
260 return false; |
|
261 } |
|
262 |
|
263 loaded_ = true; |
|
264 return true; |
|
265 } |
|
266 |
|
267 //---------------------------------------------------------------------- |
|
268 void |
|
269 GlobalStore::update() |
|
270 { |
|
271 ASSERT(lock_->is_locked_by_me()); |
|
272 |
|
273 log_debug("updating global store"); |
|
274 |
|
275 // make certain we don't attempt to write out globals before |
|
276 // load() has had a chance to load them from the database |
|
277 ASSERT(loaded_); |
|
278 |
|
279 int err = store_->put(oasys::StringShim(GLOBAL_KEY), globals_, 0); |
|
280 |
|
281 if (err != 0) { |
|
282 PANIC("GlobalStore::update fatal error updating database: %s", |
|
283 oasys::durable_strerror(err)); |
|
284 } |
|
285 } |
|
286 |
|
287 //---------------------------------------------------------------------- |
|
288 void |
|
289 GlobalStore::close() |
|
290 { |
|
291 // we prevent the potential for shutdown race crashes by leaving |
|
292 // the global store locked after it's been closed so other threads |
|
293 // will simply block, not crash due to a null store |
|
294 lock_->lock("GlobalStore::close"); |
|
295 |
|
296 delete store_; |
|
297 store_ = NULL; |
|
298 |
|
299 delete instance_; |
|
300 instance_ = NULL; |
|
301 } |
|
302 |
|
303 } // namespace dtn |