--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/contacts/ContactManager.cc Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,508 @@
+/*
+ * 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/util/StringBuffer.h>
+
+#include "ContactManager.h"
+#include "Contact.h"
+#include "Link.h"
+#include "bundling/BundleDaemon.h"
+#include "bundling/BundleEvent.h"
+#include "conv_layers/ConvergenceLayer.h"
+
+namespace dtn {
+
+//----------------------------------------------------------------------
+ContactManager::ContactManager()
+ : BundleEventHandler("ContactManager", "/dtn/contact/manager"),
+ opportunistic_cnt_(0)
+{
+ links_ = new LinkSet();
+}
+
+//----------------------------------------------------------------------
+ContactManager::~ContactManager()
+{
+ delete links_;
+}
+
+//----------------------------------------------------------------------
+bool
+ContactManager::add_new_link(const LinkRef& link)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::add_new_link");
+
+ ASSERT(link != NULL);
+ ASSERT(!link->isdeleted());
+
+ log_debug("adding NEW link %s", link->name());
+ if (has_link(link->name())) {
+ return false;
+ }
+ links_->insert(LinkRef(link.object(), "ContactManager"));
+
+ if (!link->is_create_pending()) {
+ log_debug("posting LinkCreatedEvent");
+ BundleDaemon::post(new LinkCreatedEvent(link));
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::del_link(const LinkRef& link, bool wait,
+ ContactEvent::reason_t reason)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::del_link");
+ ASSERT(link != NULL);
+
+ if (!has_link(link)) {
+ log_err("ContactManager::del_link: link %s does not exist",
+ link->name());
+ return;
+ }
+ ASSERT(!link->isdeleted());
+
+ log_debug("ContactManager::del_link: deleting link %s", link->name());
+
+ if (!wait)
+ link->delete_link();
+
+ // Close the link if it is open or in the process of being opened.
+ if (link->isopen() || link->isopening()) {
+ BundleDaemon::instance()->post(
+ new LinkStateChangeRequest(link, Link::CLOSED, reason));
+ }
+
+ // Cancel the link's availability timer (if one exists).
+ AvailabilityTimerMap::iterator iter = availability_timers_.find(link);
+ if (iter != availability_timers_.end()) {
+ LinkAvailabilityTimer* timer = iter->second;
+ availability_timers_.erase(link);
+
+ // Attempt to cancel the timer, relying on the timer system to clean
+ // up the timer state once it bubbles to the top of the timer queue.
+ // If the timer is in the process of firing (i.e., race condition),
+ // the timer should clean itself up in the timeout handler.
+ if (!timer->cancel()) {
+ log_warn("ContactManager::del_link: "
+ "failed to cancel availability timer -- race condition");
+ }
+ }
+
+ links_->erase(link);
+
+ if (wait) {
+ l.unlock();
+ // If some parent calling del_link already locked the Contact Manager,
+ // the lock will remain locked, and an event ahead of the
+ // LinkDeletedEvent may wait for the lock, causing deadlock
+ ASSERT(!lock()->is_locked_by_me());
+ oasys::Notifier notifier("ContactManager::del_link");
+ BundleDaemon::post_and_wait(new LinkDeletedEvent(link), ¬ifier);
+ link->delete_link();
+ } else {
+ BundleDaemon::post(new LinkDeletedEvent(link));
+ }
+}
+
+//----------------------------------------------------------------------
+bool
+ContactManager::has_link(const LinkRef& link)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::has_link");
+ ASSERT(link != NULL);
+
+ LinkSet::iterator iter = links_->find(link);
+ if (iter == links_->end())
+ return false;
+ return true;
+}
+
+//----------------------------------------------------------------------
+bool
+ContactManager::has_link(const char* name)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::has_link");
+ ASSERT(name != NULL);
+
+ LinkSet::iterator iter;
+ for (iter = links_->begin(); iter != links_->end(); ++iter) {
+ if (strcasecmp((*iter)->name(), name) == 0)
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------
+LinkRef
+ContactManager::find_link(const char* name)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::find_link");
+
+ LinkSet::iterator iter;
+ LinkRef link("ContactManager::find_link: return value");
+
+ for (iter = links_->begin(); iter != links_->end(); ++iter) {
+ if (strcasecmp((*iter)->name(), name) == 0) {
+ link = *iter;
+ ASSERT(!link->isdeleted());
+ return link;
+ }
+ }
+ return link;
+}
+
+//----------------------------------------------------------------------
+const LinkSet*
+ContactManager::links()
+{
+ ASSERTF(lock_.is_locked_by_me(),
+ "ContactManager::links must be called while holding lock");
+ return links_;
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::LinkAvailabilityTimer::timeout(const struct timeval& now)
+{
+ (void)now;
+ cm_->reopen_link(link_);
+ delete this;
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::reopen_link(const LinkRef& link)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::reopen_link");
+ ASSERT(link != NULL);
+
+ log_debug("reopen link %s", link->name());
+
+ availability_timers_.erase(link);
+
+ if (!has_link(link)) {
+ log_warn("ContactManager::reopen_link: "
+ "link %s does not exist", link->name());
+ return;
+ }
+ ASSERT(!link->isdeleted());
+
+ if (link->state() == Link::UNAVAILABLE) {
+ BundleDaemon::post(
+ new LinkStateChangeRequest(link, Link::OPEN,
+ ContactEvent::RECONNECT));
+ } else {
+ // state race (possibly due to user action)
+ log_err("availability timer fired for link %s but state is %s",
+ link->name(), Link::state_to_str(link->state()));
+ }
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_link_created(LinkCreatedEvent* event)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::handle_link_created");
+
+ LinkRef link = event->link_;
+ ASSERT(link != NULL);
+
+ if(link->isdeleted())
+ {
+ log_warn("ContactManager::handle_link_created: "
+ "link %s is being deleted", link->name());
+ return;
+ }
+
+ if (!has_link(link)) {
+ log_err("ContactManager::handle_link_created: "
+ "link %s does not exist", link->name());
+ return;
+ }
+
+ // Post initial state events; MOVED from Link::create_link().
+ link->set_initial_state();
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_link_available(LinkAvailableEvent* event)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::handle_link_available");
+
+ LinkRef link = event->link_;
+ ASSERT(link != NULL);
+
+ if(link->isdeleted())
+ {
+ log_warn("ContactManager::handle_link_available: "
+ "link %s is being deleted", link->name());
+ return;
+ }
+
+ if (!has_link(link)) {
+ log_warn("ContactManager::handle_link_available: "
+ "link %s does not exist", link->name());
+ return;
+ }
+
+ AvailabilityTimerMap::iterator iter;
+ iter = availability_timers_.find(link);
+ if (iter == availability_timers_.end()) {
+ return; // no timer for this link
+ }
+
+ LinkAvailabilityTimer* timer = iter->second;
+ availability_timers_.erase(link);
+
+ // try to cancel the timer and rely on the timer system to clean
+ // it up once it bubbles to the top of the queue... if there's a
+ // race and the timer is in the process of firing, it should clean
+ // itself up in the timeout handler.
+ if (!timer->cancel()) {
+ log_warn("ContactManager::handle_link_available: "
+ "can't cancel availability timer: race condition");
+ }
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_link_unavailable(LinkUnavailableEvent* event)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::handle_link_unavailable");
+
+ LinkRef link = event->link_;
+ ASSERT(link != NULL);
+
+ if (!has_link(link)) {
+ log_warn("ContactManager::handle_link_unavailable: "
+ "link %s does not exist", link->name());
+ return;
+ }
+
+ if(link->isdeleted())
+ {
+ log_warn("ContactManager::handle_link_unavailable: "
+ "link %s is being deleted", link->name());
+ return;
+ }
+
+ // don't do anything for links that aren't ondemand or alwayson
+ if (link->type() != Link::ONDEMAND && link->type() != Link::ALWAYSON) {
+ log_debug("ContactManager::handle_link_unavailable: "
+ "ignoring link unavailable for link of type %s",
+ link->type_str());
+ return;
+ }
+
+ // or if the link wasn't broken but instead was closed by user
+ // action or by going idle
+ if (event->reason_ == ContactEvent::USER ||
+ event->reason_ == ContactEvent::IDLE)
+ {
+ log_debug("ContactManager::handle_link_unavailable: "
+ "ignoring link unavailable due to %s",
+ event->reason_to_str(event->reason_));
+ return;
+ }
+
+ // adjust the retry interval in the link to handle backoff in case
+ // it continuously fails to open, then schedule the timer. note
+ // that if this is the first time the link is opened, the
+ // retry_interval will be initialized to zero so we set it to the
+ // minimum here. the retry interval is reset in the link open
+ // event handler.
+ if (link->retry_interval_ == 0) {
+ link->retry_interval_ = link->params().min_retry_interval_;
+ }
+
+ int timeout = link->retry_interval_;
+ link->retry_interval_ *= 2;
+ if (link->retry_interval_ > link->params().max_retry_interval_) {
+ link->retry_interval_ = link->params().max_retry_interval_;
+ }
+
+ LinkAvailabilityTimer* timer = new LinkAvailabilityTimer(this, link);
+
+ AvailabilityTimerMap::value_type val(link, timer);
+ if (availability_timers_.insert(val).second == false) {
+ log_err("ContactManager::handle_link_unavailable: "
+ "error inserting timer for link %s into table!", link->name());
+ delete timer;
+ return;
+ }
+
+ log_debug("link %s unavailable (%s): scheduling retry timer in %d seconds",
+ link->name(), event->reason_to_str(event->reason_), timeout);
+ timer->schedule_in(timeout * 1000);
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_contact_up(ContactUpEvent* event)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::handle_contact_up");
+
+ LinkRef link = event->contact_->link();
+ ASSERT(link != NULL);
+
+ if(link->isdeleted())
+ {
+ log_warn("ContactManager::handle_contact_up: "
+ "link %s is being deleted, not marking its contact up", link->name());
+ return;
+ }
+
+ if (!has_link(link)) {
+ log_warn("ContactManager::handle_contact_up: "
+ "link %s does not exist", link->name());
+ return;
+ }
+
+ if (link->type() == Link::ONDEMAND || link->type() == Link::ALWAYSON) {
+ log_debug("ContactManager::handle_contact_up: "
+ "resetting retry interval for link %s: %d -> %d",
+ link->name(),
+ link->retry_interval_,
+ link->params().min_retry_interval_);
+ link->retry_interval_ = link->params().min_retry_interval_;
+ }
+}
+
+//----------------------------------------------------------------------
+LinkRef
+ContactManager::find_link_to(ConvergenceLayer* cl,
+ const std::string& nexthop,
+ const EndpointID& remote_eid,
+ Link::link_type_t type,
+ u_int states)
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::find_link_to");
+
+ LinkSet::iterator iter;
+ LinkRef link("ContactManager::find_link_to: return value");
+
+ log_debug("find_link_to: cl %s nexthop %s remote_eid %s "
+ "type %s states 0x%x",
+ cl ? cl->name() : "ANY",
+ nexthop.c_str(), remote_eid.c_str(),
+ type == Link::LINK_INVALID ? "ANY" : Link::link_type_to_str(type),
+ states);
+
+ // make sure some sane criteria was specified
+ ASSERT((cl != NULL) ||
+ (nexthop != "") ||
+ (remote_eid != EndpointID::NULL_EID()) ||
+ (type != Link::LINK_INVALID));
+
+ for (iter = links_->begin(); iter != links_->end(); ++iter) {
+ if ( ((type == Link::LINK_INVALID) || (type == (*iter)->type())) &&
+ ((cl == NULL) || ((*iter)->clayer() == cl)) &&
+ ((nexthop == "") || (nexthop == (*iter)->nexthop())) &&
+ ((remote_eid == EndpointID::NULL_EID()) ||
+ (remote_eid == (*iter)->remote_eid())) &&
+ ((states & (*iter)->state()) != 0) )
+ {
+ link = *iter;
+ log_debug("ContactManager::find_link_to: "
+ "matched link *%p", link.object());
+ ASSERT(!link->isdeleted());
+ return link;
+ }
+ }
+
+ log_debug("ContactManager::find_link_to: no match");
+ return link;
+}
+
+//----------------------------------------------------------------------
+LinkRef
+ContactManager::new_opportunistic_link(ConvergenceLayer* cl,
+ const std::string& nexthop,
+ const EndpointID& remote_eid,
+ const std::string* link_name)
+{
+ log_debug("new_opportunistic_link: cl %s nexthop %s remote_eid %s",
+ cl->name(), nexthop.c_str(), remote_eid.c_str());
+
+ oasys::ScopeLock l(&lock_, "ContactManager::new_opportunistic_link");
+
+ // find a unique link name
+ char name[64];
+
+ if (link_name) {
+ strncpy(name, link_name->c_str(), sizeof(name));
+
+ while (find_link(name) != NULL) {
+ snprintf(name, sizeof(name), "%s-%d",
+ link_name->c_str(), opportunistic_cnt_);
+
+ opportunistic_cnt_++;
+ }
+ }
+
+ else {
+ do {
+ snprintf(name, sizeof(name), "link-%d",
+ opportunistic_cnt_);
+ opportunistic_cnt_++;
+ } while (find_link(name) != NULL);
+ }
+
+ LinkRef link = Link::create_link(name, Link::OPPORTUNISTIC, cl,
+ nexthop.c_str(), 0, NULL);
+ if (link == NULL) {
+ log_warn("ContactManager::new_opportunistic_link: "
+ "unexpected error creating opportunistic link");
+ return link;
+ }
+
+ LinkRef new_link(link.object(),
+ "ContactManager::new_opportunistic_link: return value");
+
+ new_link->set_remote_eid(remote_eid);
+
+ if (!add_new_link(new_link)) {
+ new_link->delete_link();
+ log_err("ContactManager::new_opportunistic_link: "
+ "failed to add new opportunistic link %s", new_link->name());
+ new_link = NULL;
+ }
+
+ return new_link;
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::dump(oasys::StringBuffer* buf) const
+{
+ oasys::ScopeLock l(&lock_, "ContactManager::dump");
+
+ buf->append("Links:\n");
+ LinkSet::iterator iter;
+ for (iter = links_->begin(); iter != links_->end(); ++iter) {
+ buf->appendf("*%p\n", (*iter).object());
+ }
+}
+
+} // namespace dtn