--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/cmd/LinkCommand.cc Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,544 @@
+/*
+ * 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/thread/Lock.h>
+#include <oasys/util/StringBuffer.h>
+
+#include "LinkCommand.h"
+#include "bundling/BundleEvent.h"
+#include "bundling/BundleDaemon.h"
+#include "contacts/Link.h"
+#include "contacts/ContactManager.h"
+#include "conv_layers/ConvergenceLayer.h"
+#include "naming/Scheme.h"
+
+namespace dtn {
+
+LinkCommand::LinkCommand()
+ : TclCommand("link")
+{
+ add_to_help("add <name> <next hop> <type> <conv layer> <opt=val> <opt2=val2>...",
+ "add links"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A string to define link name\n"
+ " <next hop>\n"
+ " A string to define next hop name "
+ "if CLA type is file is a filesystem, if is eth is an Ethernet "
+ "HW Address or if is tcp or udp is an IP:PORT)\n"
+ " <conv layer>\n"
+ " The CLA type\n"
+ " udp\n"
+ " tcp\n"
+ " eth\n"
+ " bt\n"
+ " serial\n"
+ " null\n"
+ " file\n"
+ " extcl\n"
+ " <type>\n"
+ " link type options\n"
+ " ALLWAYSON\n"
+ " ONDEMAND\n"
+ " SCHEDULED\n"
+ " OPPORTUNISTIC\n"
+ " <opt>:\n"
+ " mtu <number (MTU)>\n"
+ " min_retry_interval <number (seconds)>\n"
+ " max_retry_interval <number (seconds)>\n"
+ " idle_close_time <number (seconds)>\n"
+ " potential_downtime <number (seconds)>\n"
+ " prevhop_hdr <true or false>\n"
+ " cost <number (abstract cost)>\n"
+ " qlimit_bundles_high <number (bundles)>\n"
+ " qlimit_bytes_high <number (bytes)>\n"
+ " qlimit_bundles_low <number (bundles)>\n"
+ " qlimit_bytes_low <number (bytes)>\n"
+ " retry_interval <number (seconds)>\n");
+ add_to_help("open <name>", "open the link"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("close <name>", "close the link"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("delete <name>", "delete the link"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("set_available <name> <true | false>",
+ "hacky way to make the link available"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("state <name>", "return the state of a link"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("stats <name>", "dump link statistics"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("names", "print a list of existing link names"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("dump <name>", "print the list of existing links or "
+ "detailed info about a single link"
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n");
+ add_to_help("reconfigure <name> <opt=val> <opt2=val2>...",
+ "configure link options after initialization "
+ "\n"
+ " valid options:\n"
+ " <name>\n"
+ " A link name string\n"
+ " <opt>:\n"
+ " mtu <number (MTU)>\n"
+ " min_retry_interval <number (seconds)>\n"
+ " max_retry_interval <number (seconds)>\n"
+ " idle_close_time <number (seconds)>\n"
+ " potential_downtime <number (seconds)>\n"
+ " prevhop_hdr <true or false>\n"
+ " cost <number (abstract cost)>\n"
+ " qlimit_bundles_high <number (bundles)>\n"
+ " qlimit_bytes_high <number (bytes)>\n"
+ " qlimit_bundles_low <number (bundles)>\n"
+ " qlimit_bytes_low <number (bytes)>\n"
+ " retry_interval <number (seconds)>\n");
+ add_to_help("set_cl_defaults <conv layer> <opt=val> <opt2=val2>...",
+ "configure convergence layer specific default options"
+ "\n"
+ " valid options:\n"
+ " <conv layer>\n"
+ " The CLA type\n"
+ " udp\n"
+ " tcp\n"
+ " eth\n"
+ " bt\n"
+ " serial\n"
+ " null\n"
+ " file\n"
+ " extcl\n"
+ " <opt>:\n"
+ " mtu <number (MTU)>\n"
+ " min_retry_interval <number (seconds)>\n"
+ " max_retry_interval <number (seconds)>\n"
+ " idle_close_time <number (seconds)>\n"
+ " potential_downtime <number (seconds)>\n"
+ " prevhop_hdr <true or false>\n"
+ " cost <number (abstract cost)>\n"
+ " qlimit_bundles_high <number (bundles)>\n"
+ " qlimit_bytes_high <number (bytes)>\n"
+ " qlimit_bundles_low <number (bundles)>\n"
+ " qlimit_bytes_low <number (bytes)>\n"
+ " retry_interval <number (seconds)>\n\n");
+}
+/* " **Note options for add, reconfigure and set_cl_defaults:\n\n"
+ " mtu is the MTU of the link, used to control\n "
+ " proactive fragmentation.\n\n"
+ " min_retry_interval is the minimum amount to \n"
+ " wait between attempts to re-open the link \n"
+ " in seconds). Default is set by the various\n "
+ " link types but can be overridden by \n"
+ " configuration parameters.\n\n"
+ " max_retry_interval is the Maximum amount to \n"
+ " wait between attempts to re-open the link (in\n "
+ " seconds). Default is set by the various Link \n"
+ " types but can be overridden by configuration \n"
+ " parameters.\n\n"
+ " idle_close_time is seconds of idle time \n"
+ " before the link is closed. Must be zero for \n"
+ " always on links (i.e. they are never closed). \n"
+ " Default is 30 seconds for on demand links, \n"
+ " zero for opportunistic links.\n\n"
+ " potential_downtime is a Conservative estimate \n"
+ " of the maximum amount of time that the link may \n"
+ " be down during 'normal' operation. Used by \n"
+ " routing algorithms to determine how long to leave \n"
+ " bundles queued on the down link before rerouting \n"
+ " them. Default is 30 seconds.\n\n"
+ " prevhop_hdr indicates whether or not to send \n"
+ " the previous hop header on this link. Default is \n"
+ " false.\n\n"
+ " cost is the abstract cost of the link, used \n"
+ " by routing algorithms. Default is 100.\n\n"
+ " qlimit_bundles_high is the high limits on the \n"
+ " number of bundles that should be queued on the \n"
+ " link. The high limits are used to indicate whether \n"
+ " or not more bundles can be queued onto the link to \n"
+ " effect backpressure from the convergence layers.\n\n"
+ " qlimit_bytes_high and qlimit_bytes_low is the \n"
+ " high or low limits on the number of bytes that \n"
+ " should be queued on the link. The high limits \n"
+ " are used to indicate whether or not more bundles \n"
+ " can be queued onto the link to effect backpressure \n"
+ " from the convergence layers. The low limits can \n"
+ " be used by the router to determine when to re-scan \n"
+ " the pending bundle lists\n\n"
+ " retry_interval is the seconds to wait between \n"
+ " attempts to re-open an unavailable link. Initially \n"
+ " set to min_retry_interval_, then doubles up to \n"
+ " max_retry_interval");
+*/
+
+int
+LinkCommand::exec(int argc, const char** argv, Tcl_Interp* interp)
+{
+ (void)interp;
+
+ if (argc < 2) {
+ resultf("need a link subcommand");
+ return TCL_ERROR;
+ }
+
+ const char* cmd = argv[1];
+
+ if (strcmp(cmd, "add") == 0) {
+ // link add <name> <nexthop> <type> <clayer> <args>
+ if (argc < 6) {
+ wrong_num_args(argc, argv, 2, 6, INT_MAX);
+ return TCL_ERROR;
+ }
+
+ const char* name = argv[2];
+ const char* nexthop = argv[3];
+ const char* type_str = argv[4];
+ const char* cl_str = argv[5];
+
+ // validate the link type, make sure there's no link of the
+ // same name, and validate the convergence layer
+ Link::link_type_t type = Link::str_to_link_type(type_str);
+ if (type == Link::LINK_INVALID) {
+ resultf("invalid link type %s", type_str);
+ return TCL_ERROR;
+ }
+
+ ConvergenceLayer* cl = ConvergenceLayer::find_clayer(cl_str);
+ if (!cl) {
+ resultf("invalid convergence layer %s", cl_str);
+ return TCL_ERROR;
+ }
+
+ // Create the link, parsing the cl-specific next hop string
+ // and other arguments
+ LinkRef link;
+ const char* invalid_arg = "(unknown)";
+ link = Link::create_link(name, type, cl, nexthop, argc - 6, &argv[6],
+ &invalid_arg);
+ if (link == NULL) {
+ resultf("invalid link option: %s", invalid_arg);
+ return TCL_ERROR;
+ }
+
+ // Add the link to contact manager's table if it is not already
+ // present. The contact manager will post a LinkCreatedEvent to
+ // the daemon if the link is added successfully.
+ if (!BundleDaemon::instance()->contactmgr()->add_new_link(link)) {
+ // A link of that name already exists
+ link->delete_link();
+ resultf("link name %s already exists, use different name", name);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+
+ } else if (strcmp(cmd, "reconfigure") == 0) {
+ // link set_option <name> <opt=val> <opt2=val2>...
+ if (argc < 4) {
+ wrong_num_args(argc, argv, 2, 4, INT_MAX);
+ return TCL_ERROR;
+ }
+
+ const char* name = argv[2];
+
+ LinkRef link = BundleDaemon::instance()->contactmgr()->find_link(name);
+ if (link == NULL) {
+ resultf("link %s doesn't exist", name);
+ return TCL_ERROR;
+ }
+
+ argc -= 3;
+ argv += 3;
+
+ const char* invalid;
+ int count = link->parse_args(argc, argv, &invalid);
+ if (count == -1) {
+ resultf("invalid link option: %s", invalid);
+ return TCL_ERROR;
+ }
+ argc -= count;
+
+ if (!link->reconfigure_link(argc, argv)) {
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+
+ } else if (strcmp(cmd, "open") == 0) {
+ // link open <name>
+ if (argc != 3) {
+ wrong_num_args(argc, argv, 2, 3, 3);
+ return TCL_ERROR;
+ }
+
+ const char* name = argv[2];
+
+ LinkRef link = BundleDaemon::instance()->contactmgr()->find_link(name);
+ if (link == NULL) {
+ resultf("link %s doesn't exist", name);
+ return TCL_ERROR;
+ }
+
+ if (link->isopen()) {
+ resultf("link %s already open", name);
+ return TCL_OK;
+ }
+
+ // XXX/TODO should change all these to post_and_wait
+ BundleDaemon::post(new LinkStateChangeRequest(link, Link::OPEN,
+ ContactEvent::USER));
+
+ } else if (strcmp(cmd, "close") == 0) {
+ // link close <name>
+ if (argc != 3) {
+ wrong_num_args(argc, argv, 2, 3, 3);
+ return TCL_ERROR;
+ }
+
+ const char* name = argv[2];
+
+ LinkRef link = BundleDaemon::instance()->contactmgr()->find_link(name);
+ if (link == NULL) {
+ resultf("link %s doesn't exist", name);
+ return TCL_ERROR;
+ }
+
+ if (! link->isopen() && ! link->isopening()) {
+ resultf("link %s already closed", name);
+ return TCL_OK;
+ }
+
+ BundleDaemon::instance()->post(
+ new LinkStateChangeRequest(link, Link::CLOSED,
+ ContactEvent::USER));
+
+ return TCL_OK;
+
+ } else if (strcmp(cmd, "delete") == 0) {
+ // link delete <name>
+ if (argc != 3) {
+ wrong_num_args(argc, argv, 2, 3, 3);
+ return TCL_ERROR;
+ }
+
+ const char * name = argv[2];
+
+ LinkRef link = BundleDaemon::instance()->contactmgr()->find_link(name);
+ if (link == NULL) {
+ resultf("link %s doesn't exist", name);
+ return TCL_ERROR;
+ }
+
+ BundleDaemon::instance()->post(new LinkDeleteRequest(link));
+ return TCL_OK;
+
+ } else if (strcmp(cmd, "set_available") == 0) {
+ // link set_available <name> <true|false>
+ if (argc != 4) {
+ wrong_num_args(argc, argv, 2, 4, 4);
+ return TCL_ERROR;
+ }
+
+ const char* name = argv[2];
+
+ LinkRef link = BundleDaemon::instance()->contactmgr()->find_link(name);
+ if (link == NULL) {
+ resultf("link %s doesn't exist", name);
+ return TCL_ERROR;
+ }
+
+ int len = strlen(argv[3]);
+ bool set_available;
+
+ if (strncmp(argv[3], "1", len) == 0) {
+ set_available = true;
+ } else if (strncmp(argv[3], "0", len) == 0) {
+ set_available = false;
+ } else if (strncasecmp(argv[3], "true", len) == 0) {
+ set_available = true;
+ } else if (strncasecmp(argv[3], "false", len) == 0) {
+ set_available = false;
+ } else if (strncasecmp(argv[3], "on", len) == 0) {
+ set_available = true;
+ } else if (strncasecmp(argv[3], "off", len) == 0) {
+ set_available = false;
+ } else {
+ resultf("error converting argument %s to boolean value", argv[3]);
+ return TCL_ERROR;
+ }
+
+ if (set_available) {
+ if (link->state() != Link::UNAVAILABLE) {
+ resultf("link %s already in state %s",
+ name, Link::state_to_str(link->state()));
+ return TCL_OK;
+ }
+
+ BundleDaemon::post(
+ new LinkStateChangeRequest(link, Link::AVAILABLE,
+ ContactEvent::USER));
+
+ return TCL_OK;
+
+ } else { // ! set_available
+ if (link->state() != Link::AVAILABLE) {
+ resultf("link %s can't be set unavailable in state %s",
+ name, Link::state_to_str(link->state()));
+ return TCL_OK;
+ }
+
+ BundleDaemon::post(
+ new LinkStateChangeRequest(link, Link::UNAVAILABLE,
+ ContactEvent::USER));
+
+ return TCL_OK;
+ }
+ }
+ else if ((strcmp(cmd, "state") == 0) ||
+ (strcmp(cmd, "stats") == 0))
+ {
+ // link state <name>
+ // link stats <name>
+ if (argc != 3) {
+ wrong_num_args(argc, argv, 2, 3, 3);
+ return TCL_ERROR;
+ }
+
+ const char* name = argv[2];
+
+ LinkRef link = BundleDaemon::instance()->contactmgr()->find_link(name);
+ if (link == NULL) {
+ resultf("link %s doesn't exist", name);
+ return TCL_ERROR;
+ }
+
+ if (strcmp(cmd, "state") == 0) {
+ resultf("%s", Link::state_to_str(link->state()));
+ } else {
+ oasys::StringBuffer buf;
+ link->dump_stats(&buf);
+ set_result(buf.c_str());
+ }
+ return TCL_OK;
+ }
+ else if (strcmp(cmd, "names") == 0)
+ {
+ // link names
+ if (argc != 2) {
+ wrong_num_args(argc, argv, 2, 2, 2);
+ return TCL_ERROR;
+ }
+
+ ContactManager* cm = BundleDaemon::instance()->contactmgr();
+ oasys::ScopeLock l(cm->lock(), "LinkCommand::exec");
+
+ const LinkSet* links = cm->links();
+ for (LinkSet::const_iterator i = links->begin();
+ i != links->end(); ++i)
+ {
+ append_resultf("%s\n", (*i)->name());
+ }
+ return TCL_OK;
+ }
+ else if (strcmp(cmd, "dump") == 0)
+ {
+ // link dump <name?>
+ if (argc == 2) {
+ ContactManager* cm = BundleDaemon::instance()->contactmgr();
+ oasys::ScopeLock l(cm->lock(), "LinkCommand::exec");
+
+ const LinkSet* links = cm->links();
+ for (LinkSet::const_iterator i = links->begin();
+ i != links->end(); ++i)
+ {
+ append_resultf("*%p\n", (*i).object());
+ }
+ } else if (argc == 3) {
+ const char* name = argv[2];
+
+ LinkRef link =
+ BundleDaemon::instance()->contactmgr()->find_link(name);
+ if (link == NULL) {
+ resultf("link %s doesn't exist", name);
+ return TCL_ERROR;
+ }
+
+ oasys::StringBuffer buf;
+ link->dump(&buf);
+ set_result(buf.c_str());
+ return TCL_OK;
+ } else {
+ wrong_num_args(argc, argv, 2, 2, 3);
+ return TCL_ERROR;
+ }
+ }
+ else if (strcmp(cmd, "set_cl_defaults") == 0)
+ {
+ // link set_cl_defaults <cl> <opt=val> <opt2=val2> ...
+ if (argc < 4) {
+ wrong_num_args(argc, argv, 2, 4, INT_MAX);
+ return TCL_ERROR;
+ }
+
+ ConvergenceLayer* cl = ConvergenceLayer::find_clayer(argv[2]);
+ if (cl == NULL) {
+ resultf("unknown convergence layer %s", argv[2]);
+ return TCL_ERROR;
+ }
+
+ const char* invalid;
+ if (!cl->set_link_defaults(argc - 3, &argv[3], &invalid)) {
+ resultf("invalid link option: %s", invalid);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+ }
+ else
+ {
+ resultf("unimplemented link subcommand %s", cmd);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+} // namespace dtn