-
balaskoa authored
Signed-off-by:
balaskoa <Jeno.Balasko@ericsson.com> Change-Id: I9e57bdb502fbe75eeb0de12a91eec37cfc1a2df0
balaskoa authoredSigned-off-by:
balaskoa <Jeno.Balasko@ericsson.com> Change-Id: I9e57bdb502fbe75eeb0de12a91eec37cfc1a2df0
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Port.cc 89.27 KiB
/******************************************************************************
* Copyright (c) 2000-2020 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* Baji, Laszlo
* Balasko, Jeno
* Baranyi, Botond
* Delic, Adam
* Feher, Csaba
* Forstner, Matyas
* Kovacs, Ferenc
* Pandi, Krisztian
* Raduly, Csaba
* Szabados, Kristof
* Szabo, Bence Janos
* Szabo, Janos Zoltan – initial implementation
* Szalai, Gabor
* Tatarka, Gabor
*
******************************************************************************/
#include "Port.hh"
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include "../common/platform.h"
#include "../common/memory.h"
#include "Component.hh"
#include "Error.hh"
#include "Logger.hh"
#include "Event_Handler.hh"
#include "Fd_And_Timeout_User.hh"
#include "Snapshot.hh"
#include "Communication.hh"
#include "Runtime.hh"
#include "Octetstring.hh"
#include "TitanLoggerApi.hh"
// maximum number of iterations for binding the UNIX server socket
#define UNIX_BIND_MAX_ITER 100
#include "../common/dbgnew.hh"
void Map_Params::init(unsigned int p_nof_params)
{
nof_params = p_nof_params;
params = new CHARSTRING[nof_params];
}
void Map_Params::clear()
{
delete[] params;
nof_params = 0;
params = NULL;
}
void Map_Params::copy(const Map_Params& p_other)
{
init(p_other.nof_params);
for (unsigned int i = 0; i < nof_params; ++i) {
params[i] = p_other.params[i];
}
}
Map_Params& Map_Params::operator=(const Map_Params& p_other)
{
clear();
copy(p_other);
return *this;
}
void Map_Params::reset(unsigned int p_nof_params)
{
clear();
init(p_nof_params);
}
void Map_Params::set_param(unsigned int p_index, const CHARSTRING& p_param)
{
if (p_index >= nof_params) {
TTCN_error("Map/unmap parameter index out of bounds");
}
params[p_index] = p_param;
}
unsigned int Map_Params::get_nof_params() const
{
return nof_params;
}
const CHARSTRING& Map_Params::get_param(unsigned int p_index) const
{
if (p_index >= nof_params) {
TTCN_error("Map/unmap parameter index out of bounds");
}
return params[p_index];
}
Map_Params map_params_cache(0);
PORT *PORT::list_head = NULL, *PORT::list_tail = NULL;
PORT *PORT::system_list_head = NULL, *PORT::system_list_tail = NULL;
void PORT::add_to_list(boolean system)
{
PORT **head = system == FALSE ? &list_head : &system_list_head;
PORT **tail = system == FALSE ? &list_tail : &system_list_tail;
// check for duplicate names
for (PORT *p = *head; p != NULL; p = p->list_next) {
// do nothing if this is already a member of the list
if (p == this) return;
else if (!strcmp(p->port_name, port_name))
TTCN_error("Internal error: There are more than one ports with "
"name %s.", port_name);
}
// append this to the list
if (*head == NULL) *head = this;
else if (*tail != NULL) (*tail)->list_next = this;
list_prev = *tail;
list_next = NULL;
*tail = this;
}
void PORT::remove_from_list(boolean system)
{
PORT **head = system == FALSE ? &list_head : &system_list_head;
PORT **tail = system == FALSE ? &list_tail : &system_list_tail;
if (list_prev != NULL) list_prev->list_next = list_next;
else if (*head == this) *head = list_next;
if (list_next != NULL) list_next->list_prev = list_prev;
else if (*tail == this) *tail = list_prev;
list_prev = NULL;
list_next = NULL;
}
PORT *PORT::lookup_by_name(const char *par_port_name, boolean system)
{
if (system == FALSE) {
for (PORT *port = list_head; port != NULL; port = port->list_next)
if (!strcmp(par_port_name, port->port_name)) return port;
return NULL;
} else {
for (PORT *port = system_list_head; port != NULL; port = port->list_next)
if (!strcmp(par_port_name, port->port_name)) return port;
return NULL;
}
}
struct PORT::port_parameter {
component_id_t component_id;
char *port_name;
char *parameter_name;
char *parameter_value;
struct port_parameter *next_par;
} *PORT::parameter_head = NULL, *PORT::parameter_tail = NULL;
void PORT::apply_parameter(port_parameter *par_ptr)
{
if (par_ptr->port_name != NULL) {
// the parameter refers to a specific port
PORT *port = lookup_by_name(par_ptr->port_name);
if (port != NULL) port->set_parameter(par_ptr->parameter_name,
par_ptr->parameter_value);
} else {
// the parameter refers to all ports (*)
for (PORT *port = list_head; port != NULL; port = port->list_next)
port->set_parameter(par_ptr->parameter_name,
par_ptr->parameter_value);
}
}
void PORT::set_system_parameters(const char *system_port)
{
for (port_parameter *par = parameter_head; par != NULL; par = par->next_par) {
if (par->component_id.id_selector == COMPONENT_ID_SYSTEM &&
(par->port_name == NULL || !strcmp(par->port_name, system_port))) {
set_parameter(par->parameter_name, par->parameter_value);
}
}
}
void PORT::add_parameter(const component_id_t& component_id,
const char *par_port_name, const char *parameter_name,
const char *parameter_value)
{
port_parameter *new_par = new port_parameter;
new_par->component_id.id_selector = component_id.id_selector;
switch (component_id.id_selector) {
case COMPONENT_ID_NAME:
new_par->component_id.id_name = mcopystr(component_id.id_name);
break;
case COMPONENT_ID_COMPREF:
new_par->component_id.id_compref = component_id.id_compref;
break;
default:
break;
}
if (par_port_name == NULL) new_par->port_name = NULL;
else new_par->port_name = mcopystr(par_port_name);
new_par->parameter_name = mcopystr(parameter_name);
new_par->parameter_value = mcopystr(parameter_value);
new_par->next_par = NULL;
if (parameter_head == NULL) parameter_head = new_par;
if (parameter_tail != NULL) parameter_tail->next_par = new_par;
parameter_tail = new_par;
}
void PORT::clear_parameters()
{
while (parameter_head != NULL) {
port_parameter *next_par = parameter_head->next_par;
if (parameter_head->component_id.id_selector == COMPONENT_ID_NAME)
Free(parameter_head->component_id.id_name);
Free(parameter_head->port_name);
Free(parameter_head->parameter_name);
Free(parameter_head->parameter_value);
delete parameter_head;
parameter_head = next_par;
}
}
void PORT::set_parameters(component component_reference,
const char *component_name)
{
for (port_parameter *par = parameter_head; par != NULL; par = par->next_par)
switch (par->component_id.id_selector) {
case COMPONENT_ID_NAME:
if (component_name != NULL &&
!strcmp(par->component_id.id_name, component_name))
apply_parameter(par);
break;
case COMPONENT_ID_COMPREF:
if (par->component_id.id_compref == component_reference)
apply_parameter(par);
break;
case COMPONENT_ID_ALL:
apply_parameter(par);
break;
default:
break;
}
}
enum connection_data_type_enum {
CONN_DATA_LAST = 0, CONN_DATA_MESSAGE = 1, CONN_DATA_CALL = 2,
CONN_DATA_REPLY = 3, CONN_DATA_EXCEPTION = 4
};
enum connection_state_enum {
CONN_IDLE, CONN_LISTENING, CONN_CONNECTED, CONN_LAST_MSG_SENT,
CONN_LAST_MSG_RCVD
};
struct port_connection : public Fd_Event_Handler {
PORT *owner_port;
connection_state_enum connection_state;
component remote_component;
char *remote_port;
transport_type_enum transport_type;
union {
struct {
PORT *port_ptr;
} local;
struct {
int comm_fd;
Text_Buf *incoming_buf;
} stream;
};
struct port_connection *list_prev, *list_next;
OCTETSTRING sliding_buffer;
virtual void Handle_Fd_Event(int fd,
boolean is_readable, boolean is_writeable, boolean is_error);
virtual ~port_connection();
virtual void log() const;
};
void port_connection::Handle_Fd_Event(int,
boolean is_readable, boolean /*is_writeable*/, boolean /*is_error*/)
{
// Note event for connection with TRANSPORT_LOCAL transport_type
// may not arrive.
if (transport_type == TRANSPORT_INET_STREAM
|| transport_type == TRANSPORT_UNIX_STREAM
) {
if (is_readable) {
if (connection_state == CONN_LISTENING)
owner_port->handle_incoming_connection(this);
else owner_port->handle_incoming_data(this);
}
} else
TTCN_error("Internal error: Invalid transport type (%d) in port "
"connection between %s and %d:%s.", transport_type,
owner_port->get_name(), remote_component, remote_port);
}
void port_connection::log() const
{
TTCN_Logger::log_event("port connection between ");
owner_port->log(); TTCN_Logger::log_event(" and ");
TTCN_Logger::log_event(remote_component); TTCN_Logger::log_event(":");
TTCN_Logger::log_event("%s", remote_port);
}
port_connection::~port_connection()
{
if (transport_type == TRANSPORT_INET_STREAM
|| transport_type == TRANSPORT_UNIX_STREAM
) {
if (stream.comm_fd != -1) {
TTCN_warning_begin("Internal Error: File descriptor %d not "
"closed/removed in ", stream.comm_fd); log();
TTCN_warning_end();
}
}
sliding_buffer.clean_up();
}
PORT::PORT(const char *par_port_name)
{
port_name = par_port_name != NULL ? par_port_name : "<unknown>";
is_active = FALSE;
is_started = FALSE;
is_halted = FALSE;
list_prev = NULL;
list_next = NULL;
connection_list_head = NULL;
connection_list_tail = NULL;
n_system_mappings = 0;
system_mappings = NULL;
}
PORT::~PORT()
{
if (is_active) deactivate_port(FALSE); //TODO false or true
}
void PORT::set_name(const char * name)
{
if (name == NULL) TTCN_error("Internal error: Setting an "
"invalid name for a single element of a port array.");
port_name = name;
}
void PORT::log() const
{
TTCN_Logger::log_event("port %s", port_name);
}
void PORT::activate_port(boolean system)
{
if (!is_active) {
add_to_list(system);
is_active = TRUE;
msg_head_count = 0;
msg_tail_count = 0;
proc_head_count = 0;
proc_tail_count = 0;
// Only has effect when the translation port has port variables with
// default values. Only call when it is activated.
if (n_system_mappings == 0) {
init_port_variables();
}
}
}
void PORT::deactivate_port(boolean system)
{
if (is_active) {
/* In order to proceed with the deactivation we must ignore the
* following errors:
* - errors in user code of Test Port (i.e. user_stop, user_unmap)
* - failures when sending messages to MC (the link may be down)
*/
boolean is_parallel = !TTCN_Runtime::is_single();
// terminate all connections
while (connection_list_head != NULL) {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::removing__unterminated__connection,
port_name,
connection_list_head->remote_component, connection_list_head->remote_port);
if (is_parallel) {
try {
TTCN_Communication::send_disconnected(port_name,
connection_list_head->remote_component,
connection_list_head->remote_port);
} catch (const TC_Error&) { }
}
remove_connection(connection_list_head);
}
// terminate all mappings
while (n_system_mappings > 0) {
// we must make a copy of the string because unmap() will destroy it
char *system_port = mcopystr(system_mappings[0]);
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::removing__unterminated__mapping,
port_name, NULL_COMPREF, system_port);
Map_Params params(0);
try {
unmap(system_port, params, system);
} catch (const TC_Error&) { }
if (is_parallel) {
try {
TTCN_Communication::send_unmapped(port_name, system_port, params, system);
} catch (const TC_Error&) { }
}
Free(system_port);
}
// the previous disconnect/unmap operations may generate incoming events
// so we should stop and clear the queue after them
if (is_started || is_halted) {
try {
stop();
} catch (const TC_Error&) { }
}
clear_queue();
// deactivate all event handlers
Fd_And_Timeout_User::remove_all_fds(this);
Fd_And_Timeout_User::set_timer(this, 0.0);
// File descriptor events of port connections are removed
// in remove_connection
remove_from_list(system);
is_active = FALSE;
}
}
void PORT::deactivate_all()
{
while (list_head != NULL) list_head->deactivate_port(FALSE);
while (system_list_head != NULL) system_list_head->deactivate_port(TRUE);
}
void PORT::clear()
{
if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
"be cleared.", port_name);
if (!is_started && !is_halted) {
TTCN_warning("Performing clear operation on port %s, which is "
"already stopped. The operation has no effect.", port_name);
}
clear_queue();
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::port__was__cleared, port_name);
}
void PORT::all_clear()
{
for (PORT *port = list_head; port != NULL; port = port->list_next)
port->clear();
for (PORT *port = system_list_head; port != NULL; port = port->list_next)
port->clear();
}
void PORT::start()
{
if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
"be started.", port_name);
if (is_started) {
TTCN_warning("Performing start operation on port %s, which is "
"already started. The operation will clear the incoming queue.",
port_name);
clear_queue();
} else {
if (is_halted) {
// the queue might contain old messages which has to be discarded
clear_queue();
is_halted = FALSE;
}
user_start();
is_started = TRUE;
}
TTCN_Logger::log_port_state(TitanLoggerApi::Port__State_operation::started,
port_name);
}
void PORT::all_start()
{
for (PORT *port = list_head; port != NULL; port = port->list_next)
port->start();
for (PORT *port = system_list_head; port != NULL; port = port->list_next)
port->start();
}
void PORT::stop()
{
if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
"be stopped.", port_name);
if (is_started) {
is_started = FALSE;
is_halted = FALSE;
user_stop();
// dropping all messages from the queue because they cannot be
// extracted by receiving operations anymore
clear_queue();
} else if (is_halted) {
is_halted = FALSE;
clear_queue();
} else {
TTCN_warning("Performing stop operation on port %s, which is "
"already stopped. The operation has no effect.", port_name);
}
TTCN_Logger::log_port_state(TitanLoggerApi::Port__State_operation::stopped,
port_name);
}
void PORT::all_stop()
{
for (PORT *port = list_head; port != NULL; port = port->list_next)
port->stop();
for (PORT *port = system_list_head; port != NULL; port = port->list_next)
port->stop();
}
void PORT::halt()
{
if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
"be halted.", port_name);
if (is_started) {
is_started = FALSE;
is_halted = TRUE;
user_stop();
// keep the messages in the queue
} else if (is_halted) {
TTCN_warning("Performing halt operation on port %s, which is "
"already halted. The operation has no effect.", port_name);
} else {
TTCN_warning("Performing halt operation on port %s, which is "
"already stopped. The operation has no effect.", port_name);
}
TTCN_Logger::log_port_state(TitanLoggerApi::Port__State_operation::halted,
port_name);
}
void PORT::all_halt()
{
for (PORT *port = list_head; port != NULL; port = port->list_next)
port->halt();
for (PORT *port = system_list_head; port != NULL; port = port->list_next)
port->halt();
}
boolean PORT::port_is_started() {
return is_started;
}
void PORT::safe_start()
{
if (!is_started) {
activate_port(TRUE);
start();
}
}
void PORT::add_port(PORT* /*p*/)
{
TTCN_error("Internal error: Calling PORT::add_port");
}
void PORT::remove_port(PORT* /*p*/)
{
TTCN_error("Internal error: Calling PORT::remove_port");
}
PORT* PORT::get_provider_port() {
return NULL;
}
boolean PORT::incoming_message_handler(const void* message_ptr, const char* message_type,
component sender_component, const FLOAT& timestamp)
{
return FALSE;
}
alt_status PORT::receive(const COMPONENT_template&, COMPONENT *, FLOAT*, Index_Redirect*)
{
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::no__incoming__types,
TitanLoggerApi::MatchingProblemType_operation::receive__,
FALSE, FALSE, port_name);
return ALT_NO;
}
alt_status PORT::any_receive(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->receive(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Receive operation returned "
"unexpected status code on port %s while evaluating "
"`any port.receive'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::receive__,
TRUE, FALSE);
return ALT_NO;
}
}
alt_status PORT::check_receive(const COMPONENT_template&, COMPONENT *, FLOAT*,
Index_Redirect*)
{
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::no__incoming__types,
TitanLoggerApi::MatchingProblemType_operation::receive__,
FALSE, TRUE, port_name);
return ALT_NO;
}
alt_status PORT::any_check_receive(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->check_receive(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-receive operation returned "
"unexpected status code on port %s while evaluating "
"`any port.check(receive)'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::receive__,
TRUE, TRUE);
return ALT_NO;
}
}
alt_status PORT::trigger(const COMPONENT_template&, COMPONENT *, FLOAT*,
Index_Redirect*)
{
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::no__incoming__types,
TitanLoggerApi::MatchingProblemType_operation::trigger__,
FALSE, FALSE, port_name);
return ALT_NO;
}
alt_status PORT::any_trigger(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->trigger(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
case ALT_REPEAT:
return ALT_REPEAT;
default:
TTCN_error("Internal error: Trigger operation returned "
"unexpected status code on port %s while evaluating "
"`any port.trigger'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::trigger__,
TRUE, FALSE);
return ALT_NO;
}
}
alt_status PORT::getcall(const COMPONENT_template&, COMPONENT *, FLOAT*, Index_Redirect*)
{
// ToDo:Unnecessary log matching problem warning removed.
// Question: does it unnecessary?
// TTCN_Logger::log_matching_problem(
// TitanLoggerApi::MatchingProblemType_reason::no__incoming__signatures,
// TitanLoggerApi::MatchingProblemType_operation::getcall__,
// false, false, port_name);
return ALT_NO;
}
alt_status PORT::any_getcall(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->getcall(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Getcall operation returned "
"unexpected status code on port %s while evaluating "
"`any port.getcall'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::getcall__,
TRUE, FALSE);
return ALT_NO;
}
}
alt_status PORT::check_getcall(const COMPONENT_template&, COMPONENT *, FLOAT*, Index_Redirect*)
{
// ToDo:Unnecessary log matching problem warning removed.
// Question: does it unnecessary
// TTCN_Logger::log_matching_problem(
// TitanLoggerApi::MatchingProblemType_reason::no__incoming__signatures,
// TitanLoggerApi::MatchingProblemType_operation::getcall__,
// false, false, port_name);
return ALT_NO;
}
alt_status PORT::any_check_getcall(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->check_getcall(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-getcall operation returned "
"unexpected status code on port %s while evaluating "
"`any port.check(getcall)'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::getcall__,
TRUE, TRUE);
return ALT_NO;
}
}
alt_status PORT::getreply(const COMPONENT_template&, COMPONENT *, FLOAT*, Index_Redirect*)
{
// ToDo:Unnecessary log matching problem warning removed.
// Question: does it unnecessary
// TTCN_Logger::log_matching_problem(
// TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures,
// TitanLoggerApi::MatchingProblemType_operation::getreply__,
// false, false, port_name);
return ALT_NO;
}
alt_status PORT::any_getreply(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->getreply(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Getreply operation returned "
"unexpected status code on port %s while evaluating "
"`any port.getreply'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::getreply__,
TRUE, FALSE);
return ALT_NO;
}
}
alt_status PORT::check_getreply(const COMPONENT_template&, COMPONENT *, FLOAT*, Index_Redirect*)
{
// ToDo:Unnecessary log matching problem warning removed.
// Question: does it unnecessary
// TTCN_Logger::log_matching_problem(
// TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures,
// TitanLoggerApi::MatchingProblemType_operation::getreply__,
// false, true, port_name);
return ALT_NO;
}
alt_status PORT::any_check_getreply(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->check_getreply(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-getreply operation returned "
"unexpected status code on port %s while evaluating "
"`any port.check(getreply)'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::getreply__,
TRUE, TRUE);
return ALT_NO;
}
}
alt_status PORT::get_exception(const COMPONENT_template&, COMPONENT *, FLOAT*, Index_Redirect*)
{
// ToDo:Unnecessary log matching problem warning removed.
// Question: does it unnecessary
// TTCN_Logger::log_matching_problem(
// TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures__that__support__exceptions,
// TitanLoggerApi::MatchingProblemType_operation::catch__,
// false, false, port_name);
return ALT_NO;
}
alt_status PORT::any_catch(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->get_exception(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Catch operation returned "
"unexpected status code on port %s while evaluating "
"`any port.catch'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::catch__,
TRUE, FALSE);
return ALT_NO;
}
}
alt_status PORT::check_catch(const COMPONENT_template& ,
COMPONENT *, FLOAT*, Index_Redirect*)
{
// ToDo:Unnecessary log matching problem warning removed.
// Question: does it unnecessary
// TTCN_Logger::log_matching_problem(
// TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures__that__support__exceptions,
// TitanLoggerApi::MatchingProblemType_operation::catch__,
// false, TRUE, port_name);
return ALT_NO;
}
alt_status PORT::any_check_catch(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->check_catch(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-catch operation returned "
"unexpected status code on port %s while evaluating "
"`any port.check(catch)'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::catch__,
TRUE, TRUE);
return ALT_NO;
}
}
alt_status PORT::check(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect, Index_Redirect*)
{
alt_status ret_val = ALT_NO;
// the procedure-based queue must have the higher priority
switch (check_getcall(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-getcall operation returned "
"unexpected status code on port %s.", port_name);
}
if (ret_val != ALT_MAYBE) {
// don't try getreply if the procedure-based queue is empty
// (i.e. check_getcall() returned ALT_MAYBE)
switch (check_getreply(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-getreply operation returned "
"unexpected status code on port %s.", port_name);
}
}
if (ret_val != ALT_MAYBE) {
// don't try catch if the procedure-based queue is empty
// (i.e. check_getcall() or check_getreply() returned ALT_MAYBE)
switch (check_catch(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-catch operation returned "
"unexpected status code on port %s.", port_name);
}
}
switch (check_receive(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check-receive operation returned "
"unexpected status code on port %s.", port_name);
}
return ret_val;
}
alt_status PORT::any_check(const COMPONENT_template& sender_template,
COMPONENT *sender_ptr, FLOAT* timestamp_redirect)
{
if (list_head != NULL) {
alt_status ret_val = ALT_NO;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
switch (port->check(sender_template, sender_ptr, timestamp_redirect)) {
case ALT_YES:
return ALT_YES;
case ALT_MAYBE:
ret_val = ALT_MAYBE;
break;
case ALT_NO:
break;
default:
TTCN_error("Internal error: Check operation returned "
"unexpected status code on port %s while evaluating "
"`any port.check'.", port->port_name);
}
}
return ret_val;
} else {
TTCN_Logger::log_matching_problem(
TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
TitanLoggerApi::MatchingProblemType_operation::check__,
TRUE, FALSE);
return ALT_NO;
}
}
void PORT::set_parameter(const char *parameter_name, const char *)
{
TTCN_warning("Test port parameter %s is not supported on port %s.",
parameter_name, port_name);
}
void PORT::append_to_msg_queue(msg_queue_item_base* new_item)
{
new_item->next_item = NULL;
if (msg_queue_tail == NULL) msg_queue_head = new_item;
else msg_queue_tail->next_item = new_item;
msg_queue_tail = new_item;
}
void PORT::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable,
boolean is_error)
{
// The port intends to use the finer granularity event handler functions
if (is_error) {
Handle_Fd_Event_Error(fd);
if (!is_writable && !is_readable) return;
fd_event_type_enum event = Fd_And_Timeout_User::getCurReceivedEvent();
if ((event & FD_EVENT_WR) == 0) is_writable = FALSE;
if ((event & FD_EVENT_RD) == 0) is_readable = FALSE;
}
if (is_writable) {
Handle_Fd_Event_Writable(fd);
if (!is_readable) return;
if ((Fd_And_Timeout_User::getCurReceivedEvent() & FD_EVENT_RD) == 0)
return;
}
if (is_readable)
Handle_Fd_Event_Readable(fd);
}
void PORT::Handle_Fd_Event_Error(int)
{
// Silently ignore
// A port need not wait for error events
// Note: error events always cause event handler invocation
}
void PORT::Handle_Fd_Event_Writable(int)
{
TTCN_error("There is no Handle_Fd_Event_Writable member function "
"implemented in port %s. "
"This method or the Handle_Fd_Event method has to be implemented in "
"the port if the port waits for any file descriptor to be writable - "
"unless the port uses Install_Handler to specify the file descriptor "
"and timeout events for which the port waits.", port_name);
}
void PORT::Handle_Fd_Event_Readable(int)
{
TTCN_error("There is no Handle_Fd_Event_Readable member function "
"implemented in port %s. "
"This method or the Handle_Fd_Event method has to be implemented in "
"the port if the port waits for any file descriptor to be readable - "
"unless the port uses Install_Handler to specify the file descriptor "
"and timeout events for which the port waits.", port_name);
}
void PORT::Handle_Timeout(double /*time_since_last_call*/)
{
TTCN_error("There is no Handle_Timeout member function implemented in "
"port %s. "
"This method has to be implemented in the port if the port waits for "
"timeouts unless the port uses Install_Handler to specify the timeout.",
port_name);
}
void PORT::Event_Handler(const fd_set * /*read_fds*/, const fd_set * /*write_fds*/,
const fd_set * /*error_fds*/, double /*time_since_last_call*/)
{
TTCN_error("There is no Event_Handler implemented in port %s. "
"Event_Handler has to be implemented in the port if "
"Install_Handler is used to specify the file descriptor and timeout "
"events for which the port waits.", port_name);
}
void PORT::Handler_Add_Fd(int fd, Fd_Event_Type event_mask)
{
Fd_And_Timeout_User::add_fd(fd, this,
static_cast<fd_event_type_enum>(
static_cast<int>(event_mask)));
}
void PORT::Handler_Add_Fd_Read(int fd)
{
Fd_And_Timeout_User::add_fd(fd, this, FD_EVENT_RD);
}
void PORT::Handler_Add_Fd_Write(int fd)
{
Fd_And_Timeout_User::add_fd(fd, this, FD_EVENT_WR);
}
void PORT::Handler_Remove_Fd(int fd, Fd_Event_Type event_mask)
{
Fd_And_Timeout_User::remove_fd(fd, this,
static_cast<fd_event_type_enum>(
static_cast<int>(event_mask)));
}
void PORT::Handler_Remove_Fd_Read(int fd)
{
Fd_And_Timeout_User::remove_fd(fd, this, FD_EVENT_RD);
}
void PORT::Handler_Remove_Fd_Write(int fd)
{
Fd_And_Timeout_User::remove_fd(fd, this, FD_EVENT_WR);
}
void PORT::Handler_Set_Timer(double call_interval, boolean is_timeout,
boolean call_anyway, boolean is_periodic)
{
Fd_And_Timeout_User::set_timer(this, call_interval, is_timeout, call_anyway,
is_periodic);
}
void PORT::Install_Handler(const fd_set *read_fds, const fd_set *write_fds,
const fd_set *error_fds, double call_interval)
{
if (!is_active) TTCN_error("Event handler cannot be installed for "
"inactive port %s.", port_name);
if ((long) FdMap::getFdLimit() > (long) FD_SETSIZE) {
static boolean once = TRUE;
if (once) {
TTCN_warning("The maximum number of open file descriptors (%i)"
" is greater than FD_SETSIZE (%li)."
" Ensure that Test Ports using Install_Handler do not try to"
" wait for events of file descriptors with values greater than"
" FD_SETSIZE (%li)."
" (Current caller of Install_Handler is \"%s\")",
FdMap::getFdLimit(), (long) FD_SETSIZE, (long) FD_SETSIZE,
port_name);
}
once = FALSE;
}
Fd_And_Timeout_User::set_fds_with_fd_sets(this, read_fds, write_fds,
error_fds);
Fd_And_Timeout_User::set_timer(this, call_interval);
}
void PORT::Uninstall_Handler()
{
Fd_And_Timeout_User::remove_all_fds(this);
Fd_And_Timeout_User::set_timer(this, 0.0);
}
void PORT::user_map(const char *system_port)
{
// call the new user_map function if the legacy version is not overridden in
// the port implementation
Map_Params dummy(0);
user_map(system_port, dummy);
}
void PORT::user_unmap(const char *system_port)
{
// call the new user_unmap function if the legacy version is not overridden in
// the port implementation
Map_Params dummy(0);
user_unmap(system_port, dummy);
}
void PORT::user_map(const char *, Map_Params&)
{
}
void PORT::user_unmap(const char *, Map_Params&)
{
}
void PORT::user_start()
{
}
void PORT::user_stop()
{
}
void PORT::clear_queue()
{
}
component PORT::get_default_destination()
{
if (connection_list_head != NULL) {
if (n_system_mappings > 0) TTCN_error("Port %s has both connection(s) "
"and mapping(s). Message can be sent on it only with explicit "
"addressing.", port_name);
else if (connection_list_head->list_next != NULL) TTCN_error("Port %s "
"has more than one active connections. Message can be sent on it "
"only with explicit addressing.", port_name);
return connection_list_head->remote_component;
} else {
if (n_system_mappings > 1) {
TTCN_error("Port %s has more than one mappings. Message cannot "
"be sent on it to system.", port_name);
} else if (n_system_mappings < 1) {
TTCN_error("Port %s has neither connections nor mappings. "
"Message cannot be sent on it.", port_name);
}
return SYSTEM_COMPREF;
}
}
void PORT::prepare_message(Text_Buf& outgoing_buf, const char *message_type)
{
outgoing_buf.push_int(CONN_DATA_MESSAGE);
outgoing_buf.push_string(message_type);
}
void PORT::prepare_call(Text_Buf& outgoing_buf, const char *signature_name)
{
outgoing_buf.push_int(CONN_DATA_CALL);
outgoing_buf.push_string(signature_name);
}
void PORT::prepare_reply(Text_Buf& outgoing_buf, const char *signature_name)
{
outgoing_buf.push_int(CONN_DATA_REPLY);
outgoing_buf.push_string(signature_name);
}
void PORT::prepare_exception(Text_Buf& outgoing_buf, const char *signature_name)
{
outgoing_buf.push_int(CONN_DATA_EXCEPTION);
outgoing_buf.push_string(signature_name);
}
void PORT::send_data(Text_Buf &outgoing_buf,
const COMPONENT& destination_component)
{
if (!destination_component.is_bound()) TTCN_error("Internal error: "
"The destination component reference is unbound when sending data on "
"port %s.", port_name);
component destination_compref = (component)destination_component;
boolean is_unique;
port_connection *conn_ptr =
lookup_connection_to_compref(destination_compref, &is_unique);
if (conn_ptr == NULL)
TTCN_error("Data cannot be sent on port %s to component %d "
"because there is no connection towards component %d.", port_name,
destination_compref, destination_compref);
else if (!is_unique)
TTCN_error("Data cannot be sent on port %s to component %d "
"because there are more than one connections towards component "
"%d.", port_name, destination_compref, destination_compref);
else if (conn_ptr->connection_state != CONN_CONNECTED)
TTCN_error("Data cannot be sent on port %s to component %d "
"because the connection is not in active state.",
port_name, destination_compref);
switch (conn_ptr->transport_type) {
case TRANSPORT_LOCAL:
send_data_local(conn_ptr, outgoing_buf);
break;
case TRANSPORT_INET_STREAM:
case TRANSPORT_UNIX_STREAM:
send_data_stream(conn_ptr, outgoing_buf, FALSE);
break;
default:
TTCN_error("Internal error: Invalid transport type (%d) in port "
"connection between %s and %d:%s.", conn_ptr->transport_type,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
}
}
void PORT::process_data(port_connection *conn_ptr, Text_Buf& incoming_buf)
{
connection_data_type_enum conn_data_type =
(connection_data_type_enum)incoming_buf.pull_int().get_val();
if (conn_data_type != CONN_DATA_LAST) {
switch (conn_ptr->connection_state) {
case CONN_CONNECTED:
case CONN_LAST_MSG_SENT:
break;
case CONN_LAST_MSG_RCVD:
case CONN_IDLE:
TTCN_warning("Data arrived after the indication of connection "
"termination on port %s from %d:%s. Data is ignored.",
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
return;
default:
TTCN_error("Internal error: Connection of port %s with %d:%s has "
"invalid state (%d).", port_name, conn_ptr->remote_component,
conn_ptr->remote_port, conn_ptr->connection_state);
}
char *message_type = incoming_buf.pull_string();
try {
switch (conn_data_type) {
case CONN_DATA_MESSAGE:
if (!process_message(message_type, incoming_buf,
conn_ptr->remote_component, conn_ptr->sliding_buffer)) {
TTCN_error("Port %s does not support incoming message "
"type %s, which has arrived on the connection from "
"%d:%s.", port_name, message_type,
conn_ptr->remote_component, conn_ptr->remote_port);
}
break;
case CONN_DATA_CALL:
if (!process_call(message_type, incoming_buf,
conn_ptr->remote_component)) {
TTCN_error("Port %s does not support incoming call of "
"signature %s, which has arrived on the connection "
"from %d:%s.", port_name, message_type,
conn_ptr->remote_component, conn_ptr->remote_port);
}
break;
case CONN_DATA_REPLY:
if (!process_reply(message_type, incoming_buf,
conn_ptr->remote_component)) {
TTCN_error("Port %s does not support incoming reply of "
"signature %s, which has arrived on the connection "
"from %d:%s.", port_name, message_type,
conn_ptr->remote_component, conn_ptr->remote_port);
}
break;
case CONN_DATA_EXCEPTION:
if (!process_exception(message_type, incoming_buf,
conn_ptr->remote_component)) {
TTCN_error("Port %s does not support incoming exception "
"of signature %s, which has arrived on the connection "
"from %d:%s.", port_name, message_type,
conn_ptr->remote_component, conn_ptr->remote_port);
}
break;
default:
TTCN_error("Internal error: Data with invalid selector (%d) "
"was received on port %s from %d:%s.", conn_data_type,
port_name, conn_ptr->remote_component,
conn_ptr->remote_port);
}
} catch (...) {
// avoid memory leak
delete [] message_type;
throw;
}
delete [] message_type;
} else process_last_message(conn_ptr);
}
boolean PORT::process_message(const char *, Text_Buf&, component, OCTETSTRING&)
{
return FALSE;
}
boolean PORT::process_call(const char *, Text_Buf&, component)
{
return FALSE;
}
boolean PORT::process_reply(const char *, Text_Buf&, component)
{
return FALSE;
}
boolean PORT::process_exception(const char *, Text_Buf&, component)
{
return FALSE;
}
port_connection *PORT::add_connection(component remote_component,
const char *remote_port, transport_type_enum transport_type)
{
port_connection *conn_ptr;
for (conn_ptr = connection_list_head; conn_ptr != NULL;
conn_ptr = conn_ptr->list_next) {
if (conn_ptr->remote_component == remote_component) {
int ret_val = strcmp(conn_ptr->remote_port, remote_port);
if (ret_val == 0) return conn_ptr;
else if (ret_val > 0) break;
} else if (conn_ptr->remote_component > remote_component) break;
}
if (n_system_mappings > 0) {
TTCN_error("Connect operation cannot be performed on a mapped port (%s).", port_name);
}
port_connection *new_conn = new port_connection;
new_conn->owner_port = this;
new_conn->connection_state = CONN_IDLE;
new_conn->remote_component = remote_component;
new_conn->remote_port = mcopystr(remote_port);
new_conn->transport_type = transport_type;
new_conn->sliding_buffer = OCTETSTRING(0, 0);
switch (transport_type) {
case TRANSPORT_LOCAL:
new_conn->local.port_ptr = NULL;
break;
case TRANSPORT_INET_STREAM:
case TRANSPORT_UNIX_STREAM:
new_conn->stream.comm_fd = -1;
new_conn->stream.incoming_buf = NULL;
break;
default:
delete new_conn;
TTCN_error("Internal error: PORT::add_connection(): invalid transport "
"type (%d).", transport_type);
}
new_conn->list_next = conn_ptr;
if (conn_ptr != NULL) {
// new_conn will be inserted before conn_ptr in the ordered list
new_conn->list_prev = conn_ptr->list_prev;
conn_ptr->list_prev = new_conn;
if (new_conn->list_prev != NULL)
new_conn->list_prev->list_next = new_conn;
} else {
// new_conn will be inserted to the end of the list
new_conn->list_prev = connection_list_tail;
if (connection_list_tail != NULL)
connection_list_tail->list_next = new_conn;
connection_list_tail = new_conn;
}
if (conn_ptr == connection_list_head) connection_list_head = new_conn;
return new_conn;
}
void PORT::remove_connection(port_connection *conn_ptr)
{
Free(conn_ptr->remote_port);
switch (conn_ptr->transport_type) {
case TRANSPORT_LOCAL:
break;
case TRANSPORT_INET_STREAM:
case TRANSPORT_UNIX_STREAM:
if (conn_ptr->stream.comm_fd >= 0) {
Fd_And_Timeout_User::remove_fd(conn_ptr->stream.comm_fd, conn_ptr,
FD_EVENT_RD);
if (conn_ptr->connection_state == CONN_LISTENING &&
conn_ptr->transport_type == TRANSPORT_UNIX_STREAM)
unlink_unix_pathname(conn_ptr->stream.comm_fd);
close(conn_ptr->stream.comm_fd);
conn_ptr->stream.comm_fd = -1;
}
delete conn_ptr->stream.incoming_buf;
break;
default:
TTCN_error("Internal error: PORT::remove_connection(): invalid "
"transport type.");
}
if (conn_ptr->list_prev != NULL)
conn_ptr->list_prev->list_next = conn_ptr->list_next;
else if (connection_list_head == conn_ptr)
connection_list_head = conn_ptr->list_next;
if (conn_ptr->list_next != NULL)
conn_ptr->list_next->list_prev = conn_ptr->list_prev;
else if (connection_list_tail == conn_ptr)
connection_list_tail = conn_ptr->list_prev;
delete conn_ptr;
}
port_connection *PORT::lookup_connection_to_compref(
component remote_component, boolean *is_unique)
{
for (port_connection *conn = connection_list_head; conn != NULL;
conn = conn->list_next) {
if (conn->remote_component == remote_component) {
if (is_unique != NULL) {
port_connection *nxt = conn->list_next;
if (nxt != NULL && nxt->remote_component == remote_component)
*is_unique = FALSE;
else *is_unique = TRUE;
}
return conn;
} else if (conn->remote_component > remote_component) break;
}
return NULL;
}
port_connection *PORT::lookup_connection(component remote_component,
const char *remote_port)
{
for (port_connection *conn = connection_list_head; conn != NULL;
conn = conn->list_next) {
if (conn->remote_component == remote_component) {
int ret_val = strcmp(conn->remote_port, remote_port);
if (ret_val == 0) return conn;
else if (ret_val > 0) break;
} else if (conn->remote_component > remote_component) break;
}
return NULL;
}
void PORT::add_local_connection(PORT *other_endpoint)
{
port_connection *conn_ptr = add_connection(self, other_endpoint->port_name,
TRANSPORT_LOCAL);
conn_ptr->connection_state = CONN_CONNECTED;
conn_ptr->local.port_ptr = other_endpoint;
TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::local__connection__established,
port_name, NULL_COMPREF, other_endpoint->port_name);
}
void PORT::remove_local_connection(port_connection *conn_ptr)
{
if (conn_ptr->transport_type != TRANSPORT_LOCAL)
TTCN_error("Internal error: The transport type used by the connection "
"between port %s and %d:%s is not LOCAL.", port_name,
conn_ptr->remote_component, conn_ptr->remote_port);
PORT *other_endpoint = conn_ptr->local.port_ptr;
remove_connection(conn_ptr);
TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::local__connection__terminated,
port_name, NULL_COMPREF, other_endpoint->port_name);
}
unsigned int PORT::get_connection_hash(component local_component,
const char *local_port, component remote_component, const char *remote_port)
{
const size_t N = sizeof(unsigned int);
unsigned char hash_buffer[N];
// fill the buffer with an initial pattern
for (size_t i = 0; i < N; i++) hash_buffer[i] = i % 2 ? 0x55 : 0xAA;
// add the PID of the current process to the buffer
pid_t pid = getpid();
for (size_t i = 0; i < sizeof(pid); i++)
hash_buffer[i % N] ^= (pid >> (8 * i)) & 0xFF;
// add the local and remote component reference and port name to the buffer
for (size_t i = 0; i < sizeof(local_component); i++)
hash_buffer[(N - 1) - i % N] ^= (local_component >> (8 * i)) & 0xFF;
for (size_t i = 0; local_port[i] != '\0'; i++)
hash_buffer[(N - 1) - i % N] ^= local_port[i];
for (size_t i = 0; i < sizeof(remote_component); i++)
hash_buffer[i % N] ^= (remote_component >> (8 * i)) & 0xFF;
for (size_t i = 0; remote_port[i] != '\0'; i++)
hash_buffer[i % N] ^= remote_port[i];
// convert the buffer to an integer value
unsigned int ret_val = 0;
for (size_t i = 0; i < N; i++)
ret_val = (ret_val << 8) | hash_buffer[i];
return ret_val;
}
void PORT::unlink_unix_pathname(int socket_fd)
{
struct sockaddr_un local_addr;
// querying the local pathname used by socket_fd
socklen_type addr_len = sizeof(local_addr);
if (getsockname(socket_fd, (struct sockaddr*)&local_addr, &addr_len)) {
TTCN_warning_begin("System call getsockname() failed on UNIX socket "
"file descriptor %d.", socket_fd);
TTCN_Logger::OS_error();
TTCN_Logger::log_event_str(" The associated socket file will not be "
"removed from the file system.");
TTCN_warning_end();
} else if (local_addr.sun_family != AF_UNIX) {
TTCN_warning("System call getsockname() returned invalid address "
"family for UNIX socket file descriptor %d. The associated socket "
"file will not be removed from the file system.", socket_fd);
} else if (unlink(local_addr.sun_path)) {
if (errno != ENOENT) {
TTCN_warning_begin("System call unlink() failed when trying to "
"remove UNIX socket file %s.", local_addr.sun_path);
TTCN_Logger::OS_error();
TTCN_Logger::log_event_str(" The file will remain in the file "
"system.");
TTCN_warning_end();
} else errno = 0;
}
}
void PORT::connect_listen_inet_stream(component remote_component,
const char *remote_port)
{
// creating the TCP server socket
int server_fd = NetworkHandler::socket(TTCN_Communication::get_network_family());
if (server_fd < 0) {
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Creation of the TCP server socket failed. (%s)",
strerror(errno));
errno = 0;
return;
}
// binding the socket to an ephemeral TCP port
// using the same local IP address as the control connection to MC
IPAddress *local_addr = IPAddress::create_addr(TTCN_Communication::get_network_family());
*local_addr = *TTCN_Communication::get_local_address();
local_addr->set_port(0);
if (bind(server_fd, local_addr->get_addr(), local_addr->get_addr_len())) {
close(server_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Binding of server socket to an ephemeral TCP port "
"failed. (%s)", strerror(errno));
errno = 0;
delete local_addr;
return;
}
// zero backlog is enough since we are waiting for only one client
if (listen(server_fd, 0)) {
close(server_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Listening on an ephemeral TCP port failed. (%s)",
strerror(errno));
errno = 0;
delete local_addr;
return;
}
// querying the IP address and port number used by the TCP server
if (local_addr->getsockname(server_fd)) {
close(server_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "System call getsockname() failed on the TCP server "
"socket. (%s)", strerror(errno));
errno = 0;
delete local_addr;
return;
}
if (!TTCN_Communication::set_close_on_exec(server_fd)) {
close(server_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Setting the close-on-exec flag failed on the TCP "
"server socket.");
delete local_addr;
return;
}
port_connection *new_connection = add_connection(remote_component,
remote_port, TRANSPORT_INET_STREAM);
new_connection->connection_state = CONN_LISTENING;
new_connection->stream.comm_fd = server_fd;
Fd_And_Timeout_User::add_fd(server_fd, new_connection, FD_EVENT_RD);
TTCN_Communication::send_connect_listen_ack_inet_stream(port_name,
remote_component, remote_port, local_addr);
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::port__is__waiting__for__connection__tcp,
port_name, remote_component, remote_port);
delete local_addr;
}
void PORT::connect_listen_unix_stream(component remote_component,
const char *remote_port)
{
// creating the UNIX server socket
int server_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (server_fd < 0) {
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Creation of the UNIX server socket failed. (%s)",
strerror(errno));
errno = 0;
return;
}
// binding the socket to a temporary file
struct sockaddr_un local_addr;
// the file name is constructed using a hash function
unsigned int hash_value =
get_connection_hash(self, port_name, remote_component, remote_port);
for (unsigned int i = 1; ; i++) {
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sun_family = AF_UNIX;
snprintf(local_addr.sun_path, sizeof(local_addr.sun_path),
"/tmp/ttcn3-portconn-%x", hash_value);
if (bind(server_fd, (struct sockaddr*)&local_addr, sizeof(local_addr))
== 0) {
// the operation was successful, jump out of the loop
break;
} else if (errno == EADDRINUSE) {
// the temporary file name is already used by someone else
errno = 0;
if (i < UNIX_BIND_MAX_ITER) {
// try another hash value
hash_value++;
} else {
close(server_fd);
TTCN_Communication::send_connect_error(port_name,
remote_component, remote_port, "Could not find a free "
"pathname to bind the UNIX server socket to after %u "
"iterations.", i);
errno = 0;
return;
}
} else {
close(server_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Binding of UNIX server socket to pathname %s "
"failed. (%s)", local_addr.sun_path, strerror(errno));
errno = 0;
return;
}
}
// zero backlog is enough since we are waiting for only one client
if (listen(server_fd, 0)) {
close(server_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Listening on UNIX pathname %s failed. (%s)",
local_addr.sun_path, strerror(errno));
errno = 0;
return;
}
if (!TTCN_Communication::set_close_on_exec(server_fd)) {
close(server_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Setting the close-on-exec flag failed on the UNIX "
"server socket.");
return;
}
port_connection *new_connection = add_connection(remote_component,
remote_port, TRANSPORT_UNIX_STREAM);
new_connection->connection_state = CONN_LISTENING;
new_connection->stream.comm_fd = server_fd;
Fd_And_Timeout_User::add_fd(server_fd, new_connection, FD_EVENT_RD);
TTCN_Communication::send_connect_listen_ack_unix_stream(port_name,
remote_component, remote_port, &local_addr);
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::port__is__waiting__for__connection__unix,
port_name, remote_component, remote_port, local_addr.sun_path);
}
void PORT::connect_local(component remote_component, const char *remote_port)
{
if (self != remote_component) {
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Message CONNECT with transport type LOCAL refers "
"to a port of another component (%d).", remote_component);
return;
}
PORT *remote_ptr = lookup_by_name(remote_port);
if (remote_ptr == NULL) {
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Port %s does not exist.", remote_port);
return;
} else if (!remote_ptr->is_active) TTCN_error("Internal error: Port %s is "
"inactive when trying to connect it to local port %s.", remote_port,
port_name);
add_local_connection(remote_ptr);
if (this != remote_ptr) remote_ptr->add_local_connection(this);
TTCN_Communication::send_connected(port_name, remote_component,
remote_port);
}
void PORT::connect_stream(component remote_component, const char *remote_port,
transport_type_enum transport_type, Text_Buf& text_buf)
{
#ifdef WIN32
again:
#endif
const char *transport_str;
int client_fd;
switch (transport_type) {
case TRANSPORT_INET_STREAM:
transport_str = "TCP";
// creating the TCP client socket
client_fd = NetworkHandler::socket(TTCN_Communication::get_network_family());
break;
case TRANSPORT_UNIX_STREAM:
transport_str = "UNIX";
// creating the UNIX client socket
client_fd = socket(PF_UNIX, SOCK_STREAM, 0);
break;
default:
TTCN_error("Internal error: PORT::connect_stream(): invalid transport "
"type (%d).", transport_type);
}
if (client_fd < 0) {
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Creation of the %s client socket failed. (%s)",
transport_str, strerror(errno));
errno = 0;
return;
}
switch (transport_type) {
case TRANSPORT_INET_STREAM: {
// connect to the IP address and port number given in message CONNECT
IPAddress *remote_addr = IPAddress::create_addr(TTCN_Communication::get_network_family());
remote_addr->pull_raw(text_buf);
#ifdef SOLARIS
if (connect(client_fd, (struct sockaddr*)remote_addr->get_addr(), remote_addr->get_addr_len())) {
#else
if (connect(client_fd, remote_addr->get_addr(), remote_addr->get_addr_len())) {
#endif
#ifdef WIN32
if (errno == EADDRINUSE) {
close(client_fd);
errno = 0;
TTCN_warning("connect() returned error code EADDRINUSE. "
"Perhaps this is a Cygwin bug. Trying to connect again.");
goto again;
}
#endif
close(client_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "TCP connection establishment failed to %s:%d. (%s)",
remote_addr->get_addr_str(), remote_addr->get_port(), strerror(errno));
errno = 0;
delete remote_addr;
return;
}
delete remote_addr;
break; }
case TRANSPORT_UNIX_STREAM: {
// connect to the UNIX pathname given in the message CONNECT
struct sockaddr_un remote_addr;
memset(&remote_addr, 0, sizeof(remote_addr));
remote_addr.sun_family = AF_UNIX;
size_t pathname_len = text_buf.pull_int().get_val();
if (pathname_len >= sizeof(remote_addr.sun_path)) {
close(client_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "The UNIX pathname used by the server socket is "
"too long. It consists of %lu bytes although it should be "
"shorter than %lu bytes to fit in the appropriate structure.",
(unsigned long) pathname_len,
(unsigned long) sizeof(remote_addr.sun_path));
return;
}
text_buf.pull_raw(pathname_len, remote_addr.sun_path);
if (connect(client_fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) {
close(client_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "UNIX socket connection establishment failed to "
"pathname %s. (%s)", remote_addr.sun_path, strerror(errno));
errno = 0;
return;
}
break; }
default:
TTCN_error("Internal error: PORT::connect_stream(): invalid transport "
"type (%d).", transport_type);
}
if (!TTCN_Communication::set_close_on_exec(client_fd)) {
close(client_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Setting the close-on-exec flag failed on the %s "
"client socket.", transport_str);
return;
}
if (!TTCN_Communication::set_non_blocking_mode(client_fd, TRUE)) {
close(client_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Setting the non-blocking mode failed on the %s "
"client socket.", transport_str);
return;
}
if (transport_type == TRANSPORT_INET_STREAM &&
!TTCN_Communication::set_tcp_nodelay(client_fd)) {
close(client_fd);
TTCN_Communication::send_connect_error(port_name, remote_component,
remote_port, "Setting the TCP_NODELAY flag failed on the TCP "
"client socket.");
return;
}
port_connection *new_connection = add_connection(remote_component,
remote_port, transport_type);
new_connection->connection_state = CONN_CONNECTED;
new_connection->stream.comm_fd = client_fd;
Fd_And_Timeout_User::add_fd(client_fd, new_connection, FD_EVENT_RD);
TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::connection__established,
port_name, remote_component, remote_port, transport_str);
}
void PORT::disconnect_local(port_connection *conn_ptr)
{
PORT *remote_ptr = conn_ptr->local.port_ptr;
remove_local_connection(conn_ptr);
if (this != remote_ptr) {
port_connection *conn2_ptr =
remote_ptr->lookup_connection(self, port_name);
if (conn2_ptr == NULL) TTCN_error("Internal error: Port %s is "
"connected with local port %s, but port %s does not have a "
"connection to %s.", port_name, remote_ptr->port_name,
remote_ptr->port_name, port_name);
else remote_ptr->remove_local_connection(conn2_ptr);
}
TTCN_Communication::send_disconnected(port_name, self,
remote_ptr->port_name);
}
void PORT::disconnect_stream(port_connection *conn_ptr)
{
switch (conn_ptr->connection_state) {
case CONN_LISTENING:
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::destroying__unestablished__connection,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
remove_connection(conn_ptr);
// do not send back any acknowledgment
break;
case CONN_CONNECTED: {
TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::terminating__connection,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
Text_Buf outgoing_buf;
outgoing_buf.push_int(CONN_DATA_LAST);
if (send_data_stream(conn_ptr, outgoing_buf, TRUE)) {
// sending the last message was successful
// wait for the acknowledgment from the peer
conn_ptr->connection_state = CONN_LAST_MSG_SENT;
} else {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::sending__termination__request__failed,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
// send an acknowledgment to MC immediately to avoid deadlock
// in case of communication failure
// (i.e. when the peer does not send DISCONNECTED)
TTCN_Communication::send_disconnected(port_name,
conn_ptr->remote_component, conn_ptr->remote_port);
TTCN_warning("The last outgoing messages on port %s may be lost.",
port_name);
// destroy the connection immediately
remove_connection(conn_ptr);
}
break; }
default:
TTCN_error("The connection of port %s to %d:%s is in unexpected "
"state when trying to terminate it.", port_name,
conn_ptr->remote_component, conn_ptr->remote_port);
}
}
void PORT::send_data_local(port_connection *conn_ptr, Text_Buf& outgoing_data)
{
outgoing_data.rewind();
PORT *dest_ptr = conn_ptr->local.port_ptr;
if (this != dest_ptr) {
port_connection *conn2_ptr =
dest_ptr->lookup_connection(self, port_name);
if (conn2_ptr == NULL) TTCN_error("Internal error: Port %s is "
"connected with local port %s, but port %s does not have a "
"connection to %s.", port_name, dest_ptr->port_name,
dest_ptr->port_name, port_name);
dest_ptr->process_data(conn2_ptr, outgoing_data);
} else process_data(conn_ptr, outgoing_data);
}
boolean PORT::send_data_stream(port_connection *conn_ptr,
Text_Buf& outgoing_data, boolean ignore_peer_disconnect)
{
boolean would_block_warning=FALSE;
outgoing_data.calculate_length();
const char *msg_ptr = outgoing_data.get_data();
size_t msg_len = outgoing_data.get_len(), sent_len = 0;
while (sent_len < msg_len) {
int ret_val = send(conn_ptr->stream.comm_fd, msg_ptr + sent_len,
msg_len - sent_len, 0);
if (ret_val > 0) sent_len += ret_val;
else {
switch (errno) {
case EINTR:
// a signal occurred: do nothing, just try again
errno = 0;
break;
case EAGAIN: {
// the output buffer is full: try to increase it if possible
errno = 0;
int old_bufsize, new_bufsize;
if (TTCN_Communication::increase_send_buffer(
conn_ptr->stream.comm_fd, old_bufsize, new_bufsize)) {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::sending__would__block,
port_name, conn_ptr->remote_component, conn_ptr->remote_port,
NULL, old_bufsize, new_bufsize);
} else {
if(!would_block_warning){
TTCN_warning_begin("Sending data on the connection of "
"port %s to ", port_name);
COMPONENT::log_component_reference(
conn_ptr->remote_component);
TTCN_Logger::log_event(":%s would block execution and it "
"is not possible to further increase the size of the "
"outgoing buffer. Trying to process incoming data to "
"avoid deadlock.", conn_ptr->remote_port);
TTCN_warning_end();
would_block_warning=TRUE;
}
TTCN_Snapshot::block_for_sending(conn_ptr->stream.comm_fd);
}
break; }
case ECONNRESET:
case EPIPE:
if (ignore_peer_disconnect) return FALSE;
// no break
default:
TTCN_error("Sending data on the connection of port %s to "
"%d:%s failed.", port_name, conn_ptr->remote_component,
conn_ptr->remote_port);
}
}
}
if(would_block_warning){
TTCN_warning_begin("The message finally was sent on "
"port %s to ", port_name);
COMPONENT::log_component_reference(
conn_ptr->remote_component);
TTCN_Logger::log_event(":%s.", conn_ptr->remote_port);
TTCN_warning_end();
}
return TRUE;
}
void PORT::handle_incoming_connection(port_connection *conn_ptr)
{
const char *transport_str =
conn_ptr->transport_type == TRANSPORT_INET_STREAM ? "TCP" : "UNIX";
int comm_fd = accept(conn_ptr->stream.comm_fd, NULL, NULL);
if (comm_fd < 0) {
TTCN_Communication::send_connect_error(port_name,
conn_ptr->remote_component, conn_ptr->remote_port,
"Accepting of incoming %s connection failed. (%s)", transport_str,
strerror(errno));
errno = 0;
remove_connection(conn_ptr);
return;
}
if (!TTCN_Communication::set_close_on_exec(comm_fd)) {
close(comm_fd);
TTCN_Communication::send_connect_error(port_name,
conn_ptr->remote_component, conn_ptr->remote_port,
"Setting the close-on-exec flag failed on the server-side %s "
"socket.", transport_str);
remove_connection(conn_ptr);
return;
}
if (!TTCN_Communication::set_non_blocking_mode(comm_fd, TRUE)) {
close(comm_fd);
TTCN_Communication::send_connect_error(port_name,
conn_ptr->remote_component, conn_ptr->remote_port,
"Setting the non-blocking mode failed on the server-side %s "
"socket.", transport_str);
remove_connection(conn_ptr);
return;
}
if (conn_ptr->transport_type == TRANSPORT_INET_STREAM &&
!TTCN_Communication::set_tcp_nodelay(comm_fd)) {
close(comm_fd);
TTCN_Communication::send_connect_error(port_name,
conn_ptr->remote_component, conn_ptr->remote_port,
"Setting the TCP_NODELAY flag failed on the server-side TCP "
"socket.");
remove_connection(conn_ptr);
return;
}
// shutting down the server socket and replacing it with the
// communication socket of the new connection
Fd_And_Timeout_User::remove_fd(conn_ptr->stream.comm_fd, conn_ptr,
FD_EVENT_RD);
if (conn_ptr->transport_type == TRANSPORT_UNIX_STREAM)
unlink_unix_pathname(conn_ptr->stream.comm_fd);
close(conn_ptr->stream.comm_fd);
conn_ptr->connection_state = CONN_CONNECTED;
conn_ptr->stream.comm_fd = comm_fd;
Fd_And_Timeout_User::add_fd(comm_fd, conn_ptr, FD_EVENT_RD);
TTCN_Communication::send_connected(port_name, conn_ptr->remote_component,
conn_ptr->remote_port);
TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::connection__accepted,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
}
void PORT::handle_incoming_data(port_connection *conn_ptr)
{
if (conn_ptr->stream.incoming_buf == NULL)
conn_ptr->stream.incoming_buf = new Text_Buf;
Text_Buf& incoming_buf = *conn_ptr->stream.incoming_buf;
char *buf_ptr;
int buf_len;
incoming_buf.get_end(buf_ptr, buf_len);
int recv_len = recv(conn_ptr->stream.comm_fd, buf_ptr, buf_len, 0);
if (recv_len < 0) {
// an error occurred
if (errno == ECONNRESET) {
// TCP connection was reset by peer
errno = 0;
TTCN_Communication::send_disconnected(port_name,
conn_ptr->remote_component, conn_ptr->remote_port);
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::connection__reset__by__peer,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
TTCN_warning("The last outgoing messages on port %s may be lost.",
port_name);
conn_ptr->connection_state = CONN_IDLE;
} else {
TTCN_error("Receiving data on the connection of port %s from "
"%d:%s failed.", port_name, conn_ptr->remote_component,
conn_ptr->remote_port);
}
} else if (recv_len > 0) {
// data was received
incoming_buf.increase_length(recv_len);
// processing all messages in the buffer after each other
while (incoming_buf.is_message()) {
incoming_buf.pull_int(); // message_length
process_data(conn_ptr, incoming_buf);
incoming_buf.cut_message();
}
} else {
// the connection was closed by the peer
TTCN_Communication::send_disconnected(port_name,
conn_ptr->remote_component, conn_ptr->remote_port);
if (conn_ptr->connection_state != CONN_LAST_MSG_RCVD) {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::connection__closed__by__peer,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
}
// the connection can be removed
conn_ptr->connection_state = CONN_IDLE;
}
if (conn_ptr->connection_state == CONN_IDLE) {
// terminating and removing connection
int msg_len = incoming_buf.get_len();
if (msg_len > 0) {
TTCN_warning_begin("Message fragment remained in the buffer of "
"port connection between %s and ", port_name);
COMPONENT::log_component_reference(conn_ptr->remote_component);
TTCN_Logger::log_event(":%s: ", conn_ptr->remote_port);
const unsigned char *msg_ptr =
(const unsigned char*)incoming_buf.get_data();
for (int i = 0; i < msg_len; i++)
TTCN_Logger::log_octet(msg_ptr[i]);
TTCN_warning_end();
}
TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::port__disconnected,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
remove_connection(conn_ptr);
}
}
void PORT::process_last_message(port_connection *conn_ptr)
{
switch (conn_ptr->transport_type) {
case TRANSPORT_INET_STREAM:
case TRANSPORT_UNIX_STREAM:
break;
default:
TTCN_error("Internal error: Connection termination request was "
"received on the connection of port %s with %d:%s, which has an "
"invalid transport type (%d).", port_name,
conn_ptr->remote_component, conn_ptr->remote_port,
conn_ptr->transport_type);
}
switch (conn_ptr->connection_state) {
case CONN_CONNECTED: {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::termination__request__received,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
Text_Buf outgoing_buf;
outgoing_buf.push_int(CONN_DATA_LAST);
if (send_data_stream(conn_ptr, outgoing_buf, TRUE)) {
// sending the last message was successful
// wait until the peer closes the transport connection
conn_ptr->connection_state = CONN_LAST_MSG_RCVD;
} else {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::acknowledging__termination__request__failed,
port_name, conn_ptr->remote_component, conn_ptr->remote_port);
// send an acknowledgment to MC immediately to avoid deadlock
// in case of communication failure
// (i.e. when the peer does not send DISCONNECTED)
TTCN_Communication::send_disconnected(port_name,
conn_ptr->remote_component, conn_ptr->remote_port);
// the connection can be removed immediately
TTCN_warning("The last outgoing messages on port %s may be lost.",
port_name);
conn_ptr->connection_state = CONN_IDLE;
}
break; }
case CONN_LAST_MSG_SENT:
// the connection can be removed
conn_ptr->connection_state = CONN_IDLE;
break;
case CONN_LAST_MSG_RCVD:
case CONN_IDLE:
TTCN_warning("Unexpected data arrived after the indication of "
"connection termination on port %s from %d:%s.", port_name,
conn_ptr->remote_component, conn_ptr->remote_port);
break;
default:
TTCN_error("Internal error: Connection of port %s with %d:%s has "
"invalid state (%d).", port_name, conn_ptr->remote_component,
conn_ptr->remote_port, conn_ptr->connection_state);
}
}
void PORT::map(const char *system_port, Map_Params& params, boolean translation)
{
if (!is_active) TTCN_error("Inactive port %s cannot be mapped.", port_name);
int new_posn;
for (new_posn = 0; new_posn < n_system_mappings; new_posn++) {
int str_diff = strcmp(system_port, system_mappings[new_posn]);
if (str_diff < 0) break;
else if (str_diff == 0) {
if (translation == FALSE) {
TTCN_warning("Port %s is already mapped to system:%s."
" Map operation was ignored.", port_name, system_port);
} else {
TTCN_warning("System:%s is already mapped to port %s."
" Map operation was ignored.", system_port, port_name);
}
return;
}
}
if (translation == FALSE) {
set_system_parameters(system_port);
} else {
set_system_parameters(port_name);
}
if (params.get_nof_params() == 0) {
// call the legacy function if there are no parameters (for backward compatibility)
user_map(system_port);
}
else {
user_map(system_port, params);
}
if (translation == FALSE) {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::port__was__mapped__to__system,
port_name, SYSTEM_COMPREF, system_port);
} else {
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::port__was__mapped__to__system,
system_port, SYSTEM_COMPREF, port_name);
}
// the mapping shall be registered in the table only if user_map() was
// successful
system_mappings = (char**)Realloc(system_mappings,
(n_system_mappings + 1) * sizeof(*system_mappings));
memmove(system_mappings + new_posn + 1, system_mappings + new_posn,
(n_system_mappings - new_posn) * sizeof(*system_mappings));
system_mappings[new_posn] = mcopystr(system_port);
n_system_mappings++;
if (n_system_mappings > 1) TTCN_warning("Port %s has now more than one "
"mappings. Message cannot be sent on it to system even with explicit "
"addressing.", port_name);
}
void PORT::unmap(const char *system_port, Map_Params& params, boolean translation)
{
int del_posn;
for (del_posn = 0; del_posn < n_system_mappings; del_posn++) {
int str_diff = strcmp(system_port, system_mappings[del_posn]);
if (str_diff == 0) break;
else if (str_diff < 0) {
del_posn = n_system_mappings;
break;
}
}
if (del_posn >= n_system_mappings) {
if (translation == FALSE) {
TTCN_warning("Port %s is not mapped to system:%s. "
"Unmap operation was ignored.", port_name, system_port);
} else {
TTCN_warning("System:%s is not mapped to port %s. "
"Unmap operation was ignored.", system_port, port_name);
}
return;
}
char *unmapped_port = system_mappings[del_posn];
// first remove the mapping from the table
n_system_mappings--;
memmove(system_mappings + del_posn, system_mappings + del_posn + 1,
(n_system_mappings - del_posn) * sizeof(*system_mappings));
system_mappings = (char**)Realloc(system_mappings,
n_system_mappings * sizeof(*system_mappings));
try {
if (params.get_nof_params() == 0) {
// call the legacy function if there are no parameters (for backward compatibility)
user_unmap(system_port);
}
else {
user_unmap(system_port, params);
}
} catch (...) {
// prevent from memory leak
Free(unmapped_port);
throw;
}
// Only valid for provider ports and standard like translation (user) ports.
// Currently the requirement is that the port needs to map only when mapped
// to one port. If it would be mapped to more ports then this call would
// remove all translation capability.
// This does not resets the 'port' variables in the port type definition, but
// the internal c++ port variables of the port type.
if (n_system_mappings == 0) {
reset_port_variables();
}
TTCN_Logger::log_port_misc(
TitanLoggerApi::Port__Misc_reason::port__was__unmapped__from__system,
port_name, SYSTEM_COMPREF, system_port);
Free(unmapped_port);
}
void PORT::reset_port_variables() {
}
void PORT::init_port_variables() {
}
void PORT::change_port_state(translation_port_state /*state*/) {
}
void PORT::process_connect_listen(const char *local_port,
component remote_component, const char *remote_port,
transport_type_enum transport_type)
{
PORT *port_ptr = lookup_by_name(local_port);
if (port_ptr == NULL) {
TTCN_Communication::send_connect_error(local_port, remote_component,
remote_port, "Port %s does not exist.", local_port);
return;
} else if (!port_ptr->is_active) {
TTCN_error("Internal error: Port %s is inactive when trying to "
"connect it to %d:%s.", local_port, remote_component, remote_port);
} else if (port_ptr->lookup_connection(remote_component, remote_port)
!= NULL) {
TTCN_Communication::send_connect_error(local_port, remote_component,
remote_port, "Port %s already has a connection towards %d:%s.",
local_port, remote_component, remote_port);
return;
} else if (port_ptr->lookup_connection_to_compref(remote_component, NULL)
!= NULL) {
TTCN_warning_begin("Port %s will have more than one connections with "
"ports of test component ", local_port);
COMPONENT::log_component_reference(remote_component);
TTCN_Logger::log_event_str(". These connections cannot be used for "
"sending even with explicit addressing.");
TTCN_warning_end();
}
switch (transport_type) {
case TRANSPORT_LOCAL:
TTCN_Communication::send_connect_error(local_port, remote_component,
remote_port, "Message CONNECT_LISTEN cannot refer to transport "
"type LOCAL.");
break;
case TRANSPORT_INET_STREAM:
port_ptr->connect_listen_inet_stream(remote_component, remote_port);
break;
case TRANSPORT_UNIX_STREAM:
port_ptr->connect_listen_unix_stream(remote_component, remote_port);
break;
default:
TTCN_Communication::send_connect_error(local_port, remote_component,
remote_port, "Message CONNECT_LISTEN refers to invalid transport "
"type (%d).", transport_type);
break;
}
}
void PORT::process_connect(const char *local_port,
component remote_component, const char *remote_port,
transport_type_enum transport_type, Text_Buf& text_buf)
{
PORT *port_ptr = lookup_by_name(local_port);
if (port_ptr == NULL) {
TTCN_Communication::send_connect_error(local_port, remote_component,
remote_port, "Port %s does not exist.", local_port);
return;
} else if (!port_ptr->is_active) {
TTCN_error("Internal error: Port %s is inactive when trying to "
"connect it to %d:%s.", local_port, remote_component, remote_port);
} else if (port_ptr->lookup_connection(remote_component, remote_port)
!= NULL) {
TTCN_Communication::send_connect_error(local_port, remote_component,
remote_port, "Port %s already has a connection towards %d:%s.",
local_port, remote_component, remote_port);
return;
} else if (port_ptr->lookup_connection_to_compref(remote_component, NULL)
!= NULL) {
TTCN_warning_begin("Port %s will have more than one connections with "
"ports of test component ", local_port);
COMPONENT::log_component_reference(remote_component);
TTCN_Logger::log_event_str(". These connections cannot be used for "
"sending even with explicit addressing.");
TTCN_warning_end();
}
switch (transport_type) {
case TRANSPORT_LOCAL:
port_ptr->connect_local(remote_component, remote_port);
break;
case TRANSPORT_INET_STREAM:
case TRANSPORT_UNIX_STREAM:
port_ptr->connect_stream(remote_component, remote_port, transport_type,
text_buf);
break;
default:
TTCN_Communication::send_connect_error(local_port, remote_component,
remote_port, "Message CONNECT refers to invalid transport type "
"(%d).", transport_type);
break;
}
}
void PORT::process_disconnect(const char *local_port,
component remote_component, const char *remote_port)
{
PORT *port_ptr = lookup_by_name(local_port);
if (port_ptr == NULL) {
TTCN_Communication::send_error("Message DISCONNECT refers to "
"non-existent local port %s.", local_port);
return;
} else if (!port_ptr->is_active) {
TTCN_error("Internal error: Port %s is inactive when trying to "
"disconnect it from %d:%s.", local_port, remote_component,
remote_port);
}
port_connection *conn_ptr = port_ptr->lookup_connection(remote_component,
remote_port);
if (conn_ptr == NULL) {
// the connection does not exist
if (self == remote_component && lookup_by_name(remote_port) == NULL) {
// the remote endpoint is in the same component,
// but it does not exist
TTCN_Communication::send_error("Message DISCONNECT refers to "
"non-existent port %s.", remote_port);
} else {
TTCN_Communication::send_disconnected(local_port, remote_component,
remote_port);
}
return;
}
switch (conn_ptr->transport_type) {
case TRANSPORT_LOCAL:
port_ptr->disconnect_local(conn_ptr);
break;
case TRANSPORT_INET_STREAM:
case TRANSPORT_UNIX_STREAM:
port_ptr->disconnect_stream(conn_ptr);
break;
default:
TTCN_error("Internal error: The connection of port %s to %d:%s has "
"invalid transport type (%d) when trying to terminate the "
"connection.", local_port, remote_component, remote_port,
conn_ptr->transport_type);
}
}
void PORT::make_local_connection(const char *src_port, const char *dest_port)
{
PORT *src_ptr = lookup_by_name(src_port);
if (src_ptr == NULL) TTCN_error("Connect operation refers to "
"non-existent port %s.", src_port);
else if (!src_ptr->is_active) TTCN_error("Internal error: Port %s is "
"inactive when trying to connect it with local port %s.", src_port,
dest_port);
else if (src_ptr->lookup_connection(MTC_COMPREF, dest_port) != NULL) {
TTCN_warning("Port %s is already connected with local port %s. "
"Connect operation had no effect.", src_port, dest_port);
return;
} else if (src_ptr->lookup_connection_to_compref(MTC_COMPREF, NULL)
!= NULL) {
TTCN_warning("Port %s will have more than one connections with local "
"ports. These connections cannot be used for communication even "
"with explicit addressing.", src_port);
}
PORT *dest_ptr = lookup_by_name(dest_port);
if (dest_ptr == NULL) TTCN_error("Connect operation refers to "
"non-existent port %s.", dest_port);
else if (!dest_ptr->is_active) TTCN_error("Internal error: Port %s is "
"inactive when trying to connect it with local port %s.", dest_port,
src_port);
src_ptr->add_local_connection(dest_ptr);
if (src_ptr != dest_ptr) dest_ptr->add_local_connection(src_ptr);
}
void PORT::terminate_local_connection(const char *src_port,
const char *dest_port)
{
PORT *src_ptr = lookup_by_name(src_port);
if (src_ptr == NULL) TTCN_error("Disconnect operation refers to "
"non-existent port %s.", src_port);
else if (!src_ptr->is_active) TTCN_error("Internal error: Port %s is "
"inactive when trying to disconnect it from local port %s.", src_port,
dest_port);
port_connection *conn_ptr = src_ptr->lookup_connection(MTC_COMPREF,
dest_port);
if (conn_ptr != NULL) {
PORT *dest_ptr = conn_ptr->local.port_ptr;
src_ptr->remove_local_connection(conn_ptr);
if (src_ptr != dest_ptr) {
if (!dest_ptr->is_active) TTCN_error("Internal error: Port %s is "
"inactive when trying to disconnect it from local port %s.",
dest_port, src_port);
port_connection *conn2_ptr =
dest_ptr->lookup_connection(MTC_COMPREF, src_port);
if (conn2_ptr == NULL) TTCN_error("Internal error: Port %s is "
"connected with local port %s, but port %s does not have a "
"connection to %s.", src_port, dest_port, dest_port, src_port);
else dest_ptr->remove_local_connection(conn2_ptr);
}
} else {
PORT *dest_ptr = lookup_by_name(dest_port);
if (dest_ptr == NULL) TTCN_error("Disconnect operation refers to "
"non-existent port %s.", dest_port);
else if (src_ptr != dest_ptr) {
if (!dest_ptr->is_active) TTCN_error("Internal error: Port %s is "
"inactive when trying to disconnect it from local port %s.",
dest_port, src_port);
else if (dest_ptr->lookup_connection(MTC_COMPREF, src_port) != NULL)
TTCN_error("Internal error: Port %s is connected with local "
"port %s, but port %s does not have a connection to %s.",
dest_port, src_port, src_port, dest_port);
}
TTCN_warning("Port %s does not have connection with local port %s. "
"Disconnect operation had no effect.", src_port, dest_port);
}
}
void PORT::map_port(const char *component_port, const char *system_port,
Map_Params& params, boolean translation)
{
if (translation == TRUE) {
TTCN_Runtime::initialize_system_port(system_port);
}
const char *port_name = translation == FALSE ? component_port : system_port;
PORT *port_ptr = lookup_by_name(port_name, translation);
if (port_ptr == NULL) TTCN_error("Map operation refers to "
"non-existent port %s.", port_name);
if (port_ptr->connection_list_head != NULL) {
TTCN_error("Map operation is not allowed on a connected port (%s).", port_name);
}
if (translation == FALSE) {
port_ptr->map(system_port, params, translation);
} else {
port_ptr->map(component_port, params, translation);
}
if (translation == TRUE) {
PORT* other_port_ptr = lookup_by_name(component_port, FALSE);
if (other_port_ptr == NULL) {
TTCN_error("Map operation refers to non-existent port %s.", port_name);
}
other_port_ptr->add_port(port_ptr);
port_ptr->add_port(other_port_ptr);
}
}
void PORT::unmap_port(const char *component_port, const char *system_port,
Map_Params& params, boolean translation)
{
if (translation == TRUE) {
TTCN_Runtime::initialize_system_port(system_port);
}
const char *port_name = translation == FALSE ? component_port : system_port;
PORT *port_ptr = lookup_by_name(port_name, translation);
if (port_ptr == NULL) TTCN_error("Unmap operation refers to "
"non-existent port %s.", port_name);
if (translation == FALSE) {
port_ptr->unmap(system_port, params, translation);
} else {
port_ptr->unmap(component_port, params, translation);
}
if (translation == TRUE) {
PORT* other_port_ptr = lookup_by_name(component_port, FALSE);
if (other_port_ptr == NULL) {
TTCN_error("Unmap operation refers to non-existent port %s.", port_name);
}
other_port_ptr->remove_port(port_ptr);
port_ptr->remove_port(other_port_ptr);
}
}
boolean PORT::check_port_state(const CHARSTRING& type) const
{
if (type == "Started") {
return is_started;
} else if (type == "Halted") {
return is_halted;
} else if (type == "Stopped") {
return (!is_started && !is_halted);
} else if (type == "Connected") {
return connection_list_head != NULL;
} else if (type == "Mapped") {
return n_system_mappings > 0;
} else if (type == "Linked") {
return (connection_list_head != NULL || n_system_mappings > 0);
}
TTCN_error("%s is not an allowed parameter of checkstate().", (const char*)type);
}
boolean PORT::any_check_port_state(const CHARSTRING& type)
{
boolean result = FALSE;
for (PORT *port = list_head; port != NULL; port = port->list_next) {
result = port->check_port_state(type);
if (result) {
return TRUE;
}
}
for (PORT *port = system_list_head; port != NULL; port = port->list_next) {
result = port->check_port_state(type);
if (result) {
return TRUE;
}
}
return FALSE;
}
boolean PORT::all_check_port_state(const CHARSTRING& type)
{
boolean result = TRUE;
for (PORT *port = list_head; port != NULL && result; port = port->list_next) {
result = port->check_port_state(type);
}
for (PORT *port = system_list_head; port != NULL && result; port = port->list_next) {
result = port->check_port_state(type);
}
return result;
}