--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/reg/TclRegistration.cc Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,380 @@
+/*
+ * 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 <climits>
+#include <oasys/serialize/TclListSerialize.h>
+#include <oasys/util/ScratchBuffer.h>
+
+#include "TclRegistration.h"
+#include "bundling/BundleEvent.h"
+#include "bundling/BundleDaemon.h"
+#include "bundling/BundleList.h"
+#include "bundling/BundleStatusReport.h"
+#include "bundling/CustodySignal.h"
+#include "storage/GlobalStore.h"
+
+namespace dtn {
+
+TclRegistration::TclRegistration(const EndpointIDPattern& endpoint,
+ Tcl_Interp* interp)
+
+ : Registration(GlobalStore::instance()->next_regid(),
+ endpoint, Registration::DEFER, 0, 0) // XXX/demmer expiration??
+{
+ (void)interp;
+
+ logpathf("/dtn/registration/tcl/%d", regid_);
+ set_active(true);
+
+ log_info("new tcl registration on endpoint %s", endpoint.c_str());
+
+ bundle_list_ = new BlockingBundleList(logpath_);
+ int fd = bundle_list_->notifier()->read_fd();
+ notifier_channel_ = oasys::TclCommandInterp::instance()->
+ register_file_channel((ClientData)fd, TCL_READABLE);
+ log_debug("notifier_channel_ is %p", notifier_channel_);
+}
+
+void
+TclRegistration::deliver_bundle(Bundle* bundle)
+{
+ bundle_list_->push_back(bundle);
+}
+
+int
+TclRegistration::exec(int argc, const char** argv, Tcl_Interp* interp)
+{
+ oasys::TclCommandInterp* cmdinterp = oasys::TclCommandInterp::instance();
+ if (argc < 1) {
+ cmdinterp->wrong_num_args(argc, argv, 0, 1, INT_MAX);
+ return TCL_ERROR;
+ }
+ const char* op = argv[0];
+
+ if (strcmp(op, "get_list_channel") == 0)
+ {
+ return get_list_channel(interp);
+ }
+ else if (strcmp(op, "get_bundle_data") == 0)
+ {
+ return get_bundle_data(interp);
+ }
+ else
+ {
+ cmdinterp->resultf("invalid operation '%s'", op);
+ return TCL_ERROR;
+ }
+}
+
+int
+TclRegistration::get_list_channel(Tcl_Interp* interp)
+{
+ (void)interp;
+ oasys::TclCommandInterp* cmdinterp = oasys::TclCommandInterp::instance();
+ cmdinterp->set_result(Tcl_GetChannelName(notifier_channel_));
+ return TCL_OK;
+}
+
+
+int
+TclRegistration::get_bundle_data(Tcl_Interp* interp)
+{
+ oasys::TclCommandInterp* cmdinterp = oasys::TclCommandInterp::instance();
+ BundleRef b("TclRegistration::get_bundle_data temporary");
+ b = bundle_list_->pop_front();
+ if (b == NULL) {
+ cmdinterp->set_objresult(Tcl_NewListObj(0, 0));
+ return TCL_OK; // empty list
+ }
+
+ Tcl_Obj* result;
+ int ok = parse_bundle_data(interp, b, &result);
+ cmdinterp->set_objresult(result);
+
+ if (ok != TCL_OK) {
+ return ok;
+ }
+
+ BundleDaemon::post(new BundleDeliveredEvent(b.object(), this));
+ return TCL_OK;
+
+}
+
+/**
+ * Return a Tcl list key-value pairs detailing bundle contents to a
+ * registered procedure each time a bundle arrives. The returned TCL
+ * list is suitable for assigning to an array, e.g.
+ * array set b $bundle_info
+ *
+ * Using the TclListSerialize class, all fields that are serialized in
+ * Bundle::serialize will be present in the returned tcl list.
+ *
+ * ADMIN-BUNDLE-ONLY KEY-VALUE PAIRS:
+ *
+ * admin_type : the Admin Type (the following pairs are only defined
+ * if the admin_type = "Stauts Report")
+ * reason_code : Reason Code string
+ * orig_creation_ts : creation timestamp of original bundle
+ * orig_source : EID of the original bundle's source
+ *
+ * ADMIN-BUNDLE-ONLY OPTIONAL KEY-VALUE PAIRS:
+ *
+ * orig_frag_offset : Offset of fragment
+ * orig_frag_length : Length of original bundle
+ *
+ * STATUS-REPORT-ONLY OPTIONAL KEY-VALUE PAIRS
+ *
+ * (Note that the presence of timestamp keys implies a corresponding
+ * flag has been set true. For example if forwarded_time is returned
+ * the Bundle Forwarded status flag was set; if frag_offset and
+ * frag_length are returned the ACK'ed bundle was fragmented.)
+ *
+ * sr_reason : status report reason code
+ * sr_received_time : bundle reception timestamp
+ * sr_custody_time : bundle custody transfer timestamp
+ * sr_forwarded_time : bundle forwarding timestamp
+ * sr_delivered_time : bundle delivery timestamp
+ * sr_deletion_time : bundle deletion timestamp
+ *
+ * CUSTODY-SIGNAL-ONLY OPTIONAL KEY-VALUE PAIRS
+ *
+ * custody_succeeded : boolean if custody transfer succeeded
+ * custody_reason : reason information
+ * custody_signal_time : custody transfer time
+ */
+int
+TclRegistration::parse_bundle_data(Tcl_Interp* interp,
+ const BundleRef& b,
+ Tcl_Obj** result)
+{
+ // Using the tcl based serializer, grab all the serialized
+ // metadata fields into a new list object
+ Tcl_Obj* objv = Tcl_NewListObj(0, 0);
+ oasys::TclListSerialize s(interp, objv,
+ oasys::Serialize::CONTEXT_LOCAL,
+ 0);
+ b->serialize(&s);
+
+ // read in all the payload data (XXX/demmer this will not be nice
+ // for big bundles)
+ size_t payload_len = b->payload().length();
+ oasys::ScratchBuffer<u_char*> payload_buf;
+ const u_char* payload_data = (const u_char*)"";
+ if (payload_len != 0) {
+ payload_data = b->payload().read_data(0, payload_len,
+ payload_buf.buf(payload_len));
+ }
+
+ char tmp_buf[128]; // used for sprintf strings
+
+#define addElement(e) \
+ if (Tcl_ListObjAppendElement(interp, objv, (e)) != TCL_OK) { \
+ *result = Tcl_NewStringObj("Tcl_ListObjAppendElement failed", -1); \
+ return TCL_ERROR; \
+ }
+
+ // stick in the payload
+ addElement(Tcl_NewStringObj("payload_len", -1));
+ addElement(Tcl_NewIntObj(payload_len));
+
+ addElement(Tcl_NewStringObj("payload_data", -1));
+ addElement(Tcl_NewByteArrayObj(const_cast<u_char*>(payload_data),
+ payload_len));
+
+ // and a pretty formatted creation timestamp
+ addElement(Tcl_NewStringObj("creation_ts", -1));
+ sprintf(tmp_buf, "%llu.%llu", b->creation_ts().seconds_, b->creation_ts().seqno_);
+ addElement(Tcl_NewStringObj(tmp_buf, -1));
+
+ // If we're not an admin bundle, we're done
+ if (!b->is_admin()) {
+ goto done;
+ }
+
+ // Admin Type:
+ addElement(Tcl_NewStringObj("admin_type", -1));
+ BundleProtocol::admin_record_type_t admin_type;
+ if (!BundleProtocol::get_admin_type(b.object(), &admin_type)) {
+ goto done;
+ }
+
+ // Now for each type of admin bundle, first append the string to
+ // define that type, then all the relevant fields of the type
+ switch (admin_type) {
+ case BundleProtocol::ADMIN_STATUS_REPORT:
+ {
+ addElement(Tcl_NewStringObj("Status Report", -1));
+
+ BundleStatusReport::data_t sr;
+ if (!BundleStatusReport::parse_status_report(&sr, payload_data,
+ payload_len)) {
+ *result =
+ Tcl_NewStringObj("Admin Bundle Status Report parsing failed", -1);
+ return TCL_ERROR;
+ }
+
+ // Fragment fields
+ if (sr.admin_flags_ & BundleProtocol::ADMIN_IS_FRAGMENT) {
+ addElement(Tcl_NewStringObj("orig_frag_offset", -1));
+ addElement(Tcl_NewLongObj(sr.orig_frag_offset_));
+ addElement(Tcl_NewStringObj("orig_frag_length", -1));
+ addElement(Tcl_NewLongObj(sr.orig_frag_length_));
+ }
+
+ // Status fields with timestamps:
+#define APPEND_TIMESTAMP(_flag, _what, _field) \
+ if (sr.status_flags_ & BundleStatusReport::_flag) { \
+ addElement(Tcl_NewStringObj(_what, -1)); \
+ sprintf(tmp_buf, "%llu.%llu", \
+ sr._field.seconds_, sr._field.seqno_); \
+ addElement(Tcl_NewStringObj(tmp_buf, -1)); \
+ }
+
+ APPEND_TIMESTAMP(STATUS_RECEIVED,
+ "sr_received_time", receipt_tv_);
+ APPEND_TIMESTAMP(STATUS_CUSTODY_ACCEPTED,
+ "sr_custody_time", custody_tv_);
+ APPEND_TIMESTAMP(STATUS_FORWARDED,
+ "sr_forwarded_time", forwarding_tv_);
+ APPEND_TIMESTAMP(STATUS_DELIVERED,
+ "sr_delivered_time", delivery_tv_);
+ APPEND_TIMESTAMP(STATUS_DELETED,
+ "sr_deleted_time", deletion_tv_);
+ APPEND_TIMESTAMP(STATUS_ACKED_BY_APP,
+ "sr_acked_by_app_time", ack_by_app_tv_);
+#undef APPEND_TIMESTAMP
+
+ // Reason Code:
+ addElement(Tcl_NewStringObj("sr_reason", -1));
+ addElement(Tcl_NewStringObj(BundleStatusReport::reason_to_str(sr.reason_code_), -1));
+
+ // Bundle creation timestamp
+ addElement(Tcl_NewStringObj("orig_creation_ts", -1));
+ sprintf(tmp_buf, "%llu.%llu",
+ sr.orig_creation_tv_.seconds_,
+ sr.orig_creation_tv_.seqno_);
+ addElement(Tcl_NewStringObj(tmp_buf, -1));
+
+ // Status Report's Source EID:
+ addElement(Tcl_NewStringObj("orig_source", -1));
+ addElement(Tcl_NewStringObj(sr.orig_source_eid_.data(),
+ sr.orig_source_eid_.length()));
+ break;
+ }
+
+ //-------------------------------------------
+
+ case BundleProtocol::ADMIN_CUSTODY_SIGNAL:
+ {
+ addElement(Tcl_NewStringObj("Custody Signal", -1));
+
+ CustodySignal::data_t cs;
+ if (!CustodySignal::parse_custody_signal(&cs, payload_data,
+ payload_len))
+ {
+ *result = Tcl_NewStringObj("Admin Custody Signal parsing failed", -1);
+ return TCL_ERROR;
+ }
+
+ // Fragment fields
+ if (cs.admin_flags_ & BundleProtocol::ADMIN_IS_FRAGMENT) {
+ addElement(Tcl_NewStringObj("orig_frag_offset", -1));
+ addElement(Tcl_NewLongObj(cs.orig_frag_offset_));
+ addElement(Tcl_NewStringObj("orig_frag_length", -1));
+ addElement(Tcl_NewLongObj(cs.orig_frag_length_));
+ }
+
+ addElement(Tcl_NewStringObj("custody_succeeded", -1));
+ addElement(Tcl_NewBooleanObj(cs.succeeded_));
+
+ addElement(Tcl_NewStringObj("custody_reason", -1));
+ switch(cs.reason_) {
+ case BundleProtocol::CUSTODY_NO_ADDTL_INFO:
+ addElement(Tcl_NewStringObj("No additional information.", -1));
+ break;
+
+ case BundleProtocol::CUSTODY_REDUNDANT_RECEPTION:
+ addElement(Tcl_NewStringObj("Redundant bundle reception.", -1));
+ break;
+
+ case BundleProtocol::CUSTODY_DEPLETED_STORAGE:
+ addElement(Tcl_NewStringObj("Depleted Storage.", -1));
+ break;
+
+ case BundleProtocol::CUSTODY_ENDPOINT_ID_UNINTELLIGIBLE:
+ addElement(Tcl_NewStringObj("Destination endpoint ID unintelligible.", -1));
+ break;
+
+ case BundleProtocol::CUSTODY_NO_ROUTE_TO_DEST:
+ addElement(Tcl_NewStringObj("No known route to destination from here", -1));
+ break;
+
+ case BundleProtocol::CUSTODY_NO_TIMELY_CONTACT:
+ addElement(Tcl_NewStringObj("No timely contact with next node en route.", -1));
+ break;
+
+ case BundleProtocol::CUSTODY_BLOCK_UNINTELLIGIBLE:
+ addElement(Tcl_NewStringObj("Block unintelligible.", -1));
+ break;
+
+ default:
+ sprintf(tmp_buf, "Error: Unknown Custody Signal Reason Code 0x%x",
+ cs.reason_);
+ addElement(Tcl_NewStringObj(tmp_buf, -1));
+ break;
+ }
+
+ // Custody signal timestamp
+ addElement(Tcl_NewStringObj("custody_signal_time", -1));
+ sprintf(tmp_buf, "%llu.%llu",
+ cs.custody_signal_tv_.seconds_,
+ cs.custody_signal_tv_.seqno_);
+ addElement(Tcl_NewStringObj(tmp_buf, -1));
+
+ // Bundle creation timestamp
+ addElement(Tcl_NewStringObj("orig_creation_ts", -1));
+ sprintf(tmp_buf, "%llu.%llu",
+ cs.orig_creation_tv_.seconds_,
+ cs.orig_creation_tv_.seqno_);
+ addElement(Tcl_NewStringObj(tmp_buf, -1));
+
+ // Original source eid
+ addElement(Tcl_NewStringObj("orig_source", -1));
+ addElement(Tcl_NewStringObj(cs.orig_source_eid_.data(),
+ cs.orig_source_eid_.length()));
+ break;
+ }
+
+ //-------------------------------------------
+
+ default:
+ sprintf(tmp_buf,
+ "Error: Unknown Status Report Type 0x%x", admin_type);
+ addElement(Tcl_NewStringObj(tmp_buf, -1));
+ break;
+ }
+
+ // all done
+ done:
+ *result = objv;
+ return TCL_OK;
+}
+
+} // namespace dtn