00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #ifdef HAVE_CONFIG_H
00018 # include <dtn-config.h>
00019 #endif
00020
00021 #include <climits>
00022
00023 #include <oasys/util/HexDumpBuffer.h>
00024 #include <oasys/util/StringBuffer.h>
00025 #include <oasys/util/OptParser.h>
00026
00027 #include "BundleCommand.h"
00028 #include "CompletionNotifier.h"
00029 #include "bundling/Bundle.h"
00030 #include "bundling/BundleEvent.h"
00031 #include "bundling/BundleDaemon.h"
00032 #include "reg/RegistrationTable.h"
00033 #include "reg/TclRegistration.h"
00034
00035 namespace dtn {
00036
00037 BundleCommand::BundleCommand()
00038 : TclCommand("bundle")
00039 {
00040 add_to_help("inject <src> <dst> <payload> <opt1=val1> .. <optN=valN>",
00041 "valid options:\n"
00042 " custody_xfer\n"
00043 " receive_rcpt\n"
00044 " custody_rcpt\n"
00045 " forward_rcpt\n"
00046 " delivery_rcpt\n"
00047 " deletion_rcpt\n"
00048 " expiration=integer\n"
00049 " length=integer\n");
00050 add_to_help("stats", "get statistics on the bundles");
00051 add_to_help("daemon_stats", "daemon stats");
00052 add_to_help("reset_stats", "reset currently maintained statistics");
00053 add_to_help("list", "list all of the bundles in the system");
00054 add_to_help("ids", "list the ids of all bundles the system");
00055 add_to_help("info <id>", "get info on a specific bundle");
00056 add_to_help("dump <id>", "dump a specific bundle");
00057 add_to_help("dump_tcl <id>", "dump a bundle as a tcl list");
00058 add_to_help("dump_ascii <id>", "dump the bundle in ascii");
00059 add_to_help("expire <id>", "force a specific bundle to expire");
00060 add_to_help("cancel <id> <link>", "cancel a bundle being sent on a link");
00061 add_to_help("clear_fwdlog <id>", "clear the forwarding log for a bundle");
00062 add_to_help("daemon_idle_shutdown <secs>",
00063 "shut down the bundle daemon after an idle period");
00064 }
00065
00066 BundleCommand::InjectOpts::InjectOpts()
00067 : custody_xfer_(false),
00068 receive_rcpt_(false),
00069 custody_rcpt_(false),
00070 forward_rcpt_(false),
00071 delivery_rcpt_(false),
00072 deletion_rcpt_(false),
00073 expiration_(60),
00074 length_(0),
00075 replyto_("")
00076 {}
00077
00078 bool
00079 BundleCommand::parse_inject_options(InjectOpts* options,
00080 int objc, Tcl_Obj** objv,
00081 const char** invalidp)
00082 {
00083
00084 if (objc < 6) {
00085 return true;
00086 }
00087
00088 oasys::OptParser p;
00089
00090 p.addopt(new oasys::BoolOpt("custody_xfer", &options->custody_xfer_));
00091 p.addopt(new oasys::BoolOpt("receive_rcpt", &options->receive_rcpt_));
00092 p.addopt(new oasys::BoolOpt("custody_rcpt", &options->custody_rcpt_));
00093 p.addopt(new oasys::BoolOpt("forward_rcpt", &options->forward_rcpt_));
00094 p.addopt(new oasys::BoolOpt("delivery_rcpt", &options->delivery_rcpt_));
00095 p.addopt(new oasys::BoolOpt("deletion_rcpt", &options->deletion_rcpt_));
00096 p.addopt(new oasys::UIntOpt("expiration", &options->expiration_));
00097 p.addopt(new oasys::UIntOpt("length", &options->length_));
00098 p.addopt(new oasys::StringOpt("replyto", &options->replyto_));
00099
00100 for (int i=5; i<objc; i++) {
00101 int len;
00102 const char* option_name = Tcl_GetStringFromObj(objv[i], &len);
00103 if (! p.parse_opt(option_name, len)) {
00104 *invalidp = option_name;
00105 return false;
00106 }
00107 }
00108 return true;
00109 }
00110
00111 int
00112 BundleCommand::exec(int objc, Tcl_Obj** objv, Tcl_Interp* interp)
00113 {
00114
00115 if (objc < 2) {
00116 wrong_num_args(objc, objv, 1, 2, INT_MAX);
00117 return TCL_ERROR;
00118 }
00119
00120 const char* cmd = Tcl_GetStringFromObj(objv[1], 0);
00121
00122 if (strcmp(cmd, "inject") == 0) {
00123
00124 if (objc < 5) {
00125 wrong_num_args(objc, objv, 2, 5, INT_MAX);
00126 return TCL_ERROR;
00127 }
00128
00129 bool eids_valid = true;
00130 Bundle* b = new Bundle();
00131 eids_valid &= b->mutable_source()->assign(Tcl_GetStringFromObj(objv[2], 0));
00132 eids_valid &= b->mutable_replyto()->assign(Tcl_GetStringFromObj(objv[2], 0));
00133 eids_valid &= b->mutable_dest()->assign(Tcl_GetStringFromObj(objv[3], 0));
00134 b->mutable_custodian()->assign(EndpointID::NULL_EID());
00135
00136 EndpointID::singleton_info_t info = b->dest().known_scheme() ?
00137 b->dest().is_singleton() :
00138 EndpointID::is_singleton_default_;
00139 switch (info) {
00140 case EndpointID::SINGLETON:
00141 b->set_singleton_dest(true);
00142 break;
00143 case EndpointID::MULTINODE:
00144 b->set_singleton_dest(false);
00145 break;
00146 case EndpointID::UNKNOWN:
00147 resultf("can't determine is_singleton for destination %s",
00148 b->dest().c_str());
00149 delete b;
00150 return TCL_ERROR;
00151 }
00152
00153 if (!eids_valid) {
00154 resultf("bad value for one or more EIDs");
00155 delete b;
00156 return TCL_ERROR;
00157 }
00158
00159 int payload_len;
00160 u_char* payload_data = Tcl_GetByteArrayFromObj(objv[4], &payload_len);
00161
00162
00163 InjectOpts options;
00164 const char* invalid;
00165 if (!parse_inject_options(&options, objc, objv, &invalid)) {
00166 resultf("error parsing bundle inject options: invalid option '%s'",
00167 invalid);
00168 delete b;
00169 return TCL_ERROR;
00170 }
00171
00172 b->set_custody_requested(options.custody_xfer_);
00173 b->set_receive_rcpt(options.receive_rcpt_);
00174 b->set_custody_rcpt(options.custody_rcpt_);
00175 b->set_forward_rcpt(options.forward_rcpt_);
00176 b->set_delivery_rcpt(options.delivery_rcpt_);
00177 b->set_deletion_rcpt(options.deletion_rcpt_);
00178 b->set_expiration(options.expiration_);
00179
00180
00181
00182 if (b->source() == EndpointID::NULL_EID()) {
00183 if ( b->custody_requested() ||
00184 b->receipt_requested() ||
00185 b->app_acked_rcpt() )
00186 {
00187 log_err("bundle with null source EID cannot request reports or "
00188 "custody transfer");
00189 delete b;
00190 return TCL_ERROR;
00191 }
00192
00193 b->set_do_not_fragment(true);
00194 }
00195
00196 else {
00197
00198
00199 const RegistrationTable* reg_table =
00200 BundleDaemon::instance()->reg_table();
00201 std::string base_reg_str = b->source().uri().scheme() + "://" +
00202 b->source().uri().host();
00203
00204 if (!reg_table->get(EndpointIDPattern(base_reg_str)) &&
00205 !reg_table->get(EndpointIDPattern(b->source())))
00206 {
00207 log_err("this node is not a member of the bundle's source EID (%s)",
00208 b->source().str().c_str());
00209 delete b;
00210 return TCL_ERROR;
00211 }
00212 }
00213
00214 if (options.length_ != 0) {
00215
00216
00217 b->mutable_payload()->set_length(options.length_);
00218 if (payload_len != 0) {
00219 b->mutable_payload()->write_data(payload_data, payload_len, 0);
00220 }
00221
00222
00223
00224
00225 u_char byte = 0;
00226 b->mutable_payload()->write_data(&byte, options.length_ - 1, 1);
00227
00228 payload_len = options.length_;
00229 } else {
00230
00231 b->mutable_payload()->set_data(payload_data, payload_len);
00232 }
00233
00234 if (options.replyto_ != "") {
00235 b->mutable_replyto()->assign(options.replyto_.c_str());
00236 }
00237
00238 oasys::StringBuffer error;
00239 if (!b->validate(&error)) {
00240 resultf("bundle validation failed: %s", error.data());
00241 return TCL_ERROR;
00242 }
00243
00244 log_debug("inject %d byte bundle %s->%s", payload_len,
00245 b->source().c_str(), b->dest().c_str());
00246
00247 BundleDaemon::post(new BundleReceivedEvent(b, EVENTSRC_APP));
00248
00249
00250
00251 resultf("%u.%u", b->creation_ts().seconds_, b->creation_ts().seqno_);
00252 return TCL_OK;
00253
00254 } else if (!strcmp(cmd, "stats")) {
00255 oasys::StringBuffer buf("Bundle Statistics: ");
00256 BundleDaemon::instance()->get_bundle_stats(&buf);
00257 set_result(buf.c_str());
00258 return TCL_OK;
00259
00260 } else if (!strcmp(cmd, "daemon_stats")) {
00261 oasys::StringBuffer buf("Bundle Daemon Statistics: ");
00262 BundleDaemon::instance()->get_daemon_stats(&buf);
00263 set_result(buf.c_str());
00264 return TCL_OK;
00265 } else if (!strcmp(cmd, "daemon_status")) {
00266 BundleDaemon::post_and_wait(new StatusRequest(),
00267 CompletionNotifier::notifier());
00268 set_result("DTN daemon ok");
00269 return TCL_OK;
00270 } else if (!strcmp(cmd, "reset_stats")) {
00271 BundleDaemon::instance()->reset_stats();
00272 return TCL_OK;
00273
00274 } else if (!strcmp(cmd, "list")) {
00275 Bundle* b;
00276 BundleList::iterator iter;
00277 oasys::StringBuffer buf;
00278 BundleList* pending =
00279 BundleDaemon::instance()->pending_bundles();
00280
00281 oasys::ScopeLock l(pending->lock(), "BundleCommand::exec");
00282 buf.appendf("Currently Pending Bundles (%zu): \n", pending->size());
00283
00284 for (iter = pending->begin(); iter != pending->end(); ++iter) {
00285 b = *iter;
00286 buf.appendf("\t%-3d: %s -> %s length %zu\n",
00287 b->bundleid(),
00288 b->source().c_str(),
00289 b->dest().c_str(),
00290 b->payload().length());
00291 }
00292
00293 set_result(buf.c_str());
00294
00295 return TCL_OK;
00296
00297 } else if (!strcmp(cmd, "ids")) {
00298 BundleList::iterator iter;
00299 BundleList* pending =
00300 BundleDaemon::instance()->pending_bundles();
00301
00302 oasys::ScopeLock l(pending->lock(), "BundleCommand::exec");
00303
00304 for (iter = pending->begin(); iter != pending->end(); ++iter) {
00305 append_resultf("%d ", (*iter)->bundleid());
00306 }
00307
00308 return TCL_OK;
00309
00310 } else if (!strcmp(cmd, "info") ||
00311 !strcmp(cmd, "dump") ||
00312 !strcmp(cmd, "dump_tcl") ||
00313 !strcmp(cmd, "dump_ascii") ||
00314 !strcmp(cmd, "expire"))
00315 {
00316
00317 if (objc != 3) {
00318 wrong_num_args(objc, objv, 2, 3, 3);
00319 return TCL_ERROR;
00320 }
00321
00322 int bundleid;
00323 if (Tcl_GetIntFromObj(interp, objv[2], &bundleid) != TCL_OK) {
00324 resultf("invalid bundle id %s",
00325 Tcl_GetStringFromObj(objv[2], 0));
00326 return TCL_ERROR;
00327 }
00328
00329 BundleList* pending =
00330 BundleDaemon::instance()->pending_bundles();
00331
00332 BundleRef bundle = pending->find(bundleid);
00333
00334 if (bundle == NULL) {
00335 resultf("no bundle with id %d", bundleid);
00336 return TCL_ERROR;
00337 }
00338
00339 if (strcmp(cmd, "info") == 0) {
00340 oasys::StringBuffer buf;
00341 bundle->format_verbose(&buf);
00342 set_result(buf.c_str());
00343
00344 } else if (strcmp(cmd, "dump_tcl") == 0) {
00345 Tcl_Obj* result = NULL;
00346 int ok =
00347 TclRegistration::parse_bundle_data(interp, bundle, &result);
00348
00349 set_objresult(result);
00350 return ok;
00351
00352 } else if (strcmp(cmd, "dump_ascii") == 0) {
00353 size_t len = bundle->payload().length();
00354 oasys::StringBuffer buf(len);
00355 const u_char* bp =
00356 bundle->payload().read_data(0, len, (u_char*)buf.data());
00357
00358 buf.append((const char*)bp, len);
00359 set_result(buf.c_str());
00360
00361 } else if (strcmp(cmd, "dump") == 0) {
00362 size_t len = bundle->payload().length();
00363 oasys::HexDumpBuffer buf(len);
00364
00365 bundle->payload().read_data(0, len, (u_char*)buf.tail_buf(len));
00366 buf.incr_len(len);
00367
00368 set_result(buf.hexify().c_str());
00369
00370 } else if (strcmp(cmd, "expire") == 0) {
00371 BundleDaemon::instance()->post_at_head(
00372 new BundleExpiredEvent(bundle.object()));
00373 return TCL_OK;
00374 }
00375
00376 return TCL_OK;
00377
00378 } else if (!strcmp(cmd, "cancel")) {
00379
00380 if (objc != 4) {
00381 wrong_num_args(objc, objv, 2, 4, 4);
00382 return TCL_ERROR;
00383 }
00384
00385 int bundleid;
00386 if (Tcl_GetIntFromObj(interp, objv[2], &bundleid) != TCL_OK) {
00387 resultf("invalid bundle id %s",
00388 Tcl_GetStringFromObj(objv[2], 0));
00389 return TCL_ERROR;
00390 }
00391
00392 const char* name = Tcl_GetStringFromObj(objv[3], 0);
00393
00394 BundleRef br
00395 = BundleDaemon::instance()->pending_bundles()->find(bundleid);
00396
00397 BundleDaemon::instance()->post_at_head(
00398 new BundleCancelRequest(br, name));
00399
00400 return TCL_OK;
00401
00402 } else if (!strcmp(cmd, "clear_fwdlog")) {
00403
00404 if (objc != 3) {
00405 wrong_num_args(objc, objv, 2, 3, 3);
00406 return TCL_ERROR;
00407 }
00408
00409 int bundleid;
00410 if (Tcl_GetIntFromObj(interp, objv[2], &bundleid) != TCL_OK) {
00411 resultf("invalid bundle id %s",
00412 Tcl_GetStringFromObj(objv[2], 0));
00413 return TCL_ERROR;
00414 }
00415
00416 BundleRef br
00417 = BundleDaemon::instance()->pending_bundles()->find(bundleid);
00418
00419 br->fwdlog()->clear();
00420
00421 return TCL_OK;
00422
00423 } else if (!strcmp(cmd, "daemon_idle_shutdown")) {
00424 oasys::StringBuffer buf("Bundle Daemon Statistics: ");
00425
00426 if (objc != 3) {
00427 wrong_num_args(objc, objv, 2, 3, 3);
00428 return TCL_ERROR;
00429 }
00430
00431 int interval;
00432 if (Tcl_GetIntFromObj(interp, objv[2], &interval) != TCL_OK) {
00433 resultf("invalid interval %s",
00434 Tcl_GetStringFromObj(objv[2], 0));
00435 return TCL_ERROR;
00436 }
00437
00438 BundleDaemon::instance()->init_idle_shutdown(interval);
00439 return TCL_OK;
00440
00441 } else {
00442 resultf("unknown bundle subcommand %s", cmd);
00443 return TCL_ERROR;
00444 }
00445 }
00446
00447
00448 }