Last sync 2016.04.01
[deliverable/titan.core.git] / core / Port.cc
1 /******************************************************************************
2 * Copyright (c) 2000-2016 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Baji, Laszlo
10 * Balasko, Jeno
11 * Delic, Adam
12 * Feher, Csaba
13 * Forstner, Matyas
14 * Kovacs, Ferenc
15 * Pandi, Krisztian
16 * Raduly, Csaba
17 * Szabados, Kristof
18 * Szabo, Janos Zoltan – initial implementation
19 * Szalai, Gabor
20 * Tatarka, Gabor
21 *
22 ******************************************************************************/
23 #include "Port.hh"
24
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <sys/un.h>
34
35 #include "../common/platform.h"
36
37 #include "../common/memory.h"
38 #include "Component.hh"
39 #include "Error.hh"
40 #include "Logger.hh"
41 #include "Event_Handler.hh"
42 #include "Fd_And_Timeout_User.hh"
43 #include "Snapshot.hh"
44 #include "Communication.hh"
45 #include "Runtime.hh"
46 #include "Octetstring.hh"
47 #include "TitanLoggerApi.hh"
48
49 // maximum number of iterations for binding the UNIX server socket
50 #define UNIX_BIND_MAX_ITER 100
51
52 #include "../common/dbgnew.hh"
53
54 PORT *PORT::list_head = NULL, *PORT::list_tail = NULL;
55
56 void PORT::add_to_list()
57 {
58 // check for duplicate names
59 for (PORT *p = list_head; p != NULL; p = p->list_next) {
60 // do nothing if this is already a member of the list
61 if (p == this) return;
62 else if (!strcmp(p->port_name, port_name))
63 TTCN_error("Internal error: There are more than one ports with "
64 "name %s.", port_name);
65 }
66 // append this to the list
67 if (list_head == NULL) list_head = this;
68 else if (list_tail != NULL) list_tail->list_next = this;
69 list_prev = list_tail;
70 list_next = NULL;
71 list_tail = this;
72 }
73
74 void PORT::remove_from_list()
75 {
76 if (list_prev != NULL) list_prev->list_next = list_next;
77 else if (list_head == this) list_head = list_next;
78 if (list_next != NULL) list_next->list_prev = list_prev;
79 else if (list_tail == this) list_tail = list_prev;
80 list_prev = NULL;
81 list_next = NULL;
82 }
83
84 PORT *PORT::lookup_by_name(const char *par_port_name)
85 {
86 for (PORT *port = list_head; port != NULL; port = port->list_next)
87 if (!strcmp(par_port_name, port->port_name)) return port;
88 return NULL;
89 }
90
91 struct PORT::port_parameter {
92 component_id_t component_id;
93 char *port_name;
94 char *parameter_name;
95 char *parameter_value;
96 struct port_parameter *next_par;
97 } *PORT::parameter_head = NULL, *PORT::parameter_tail = NULL;
98
99 void PORT::apply_parameter(port_parameter *par_ptr)
100 {
101 if (par_ptr->port_name != NULL) {
102 // the parameter refers to a specific port
103 PORT *port = lookup_by_name(par_ptr->port_name);
104 if (port != NULL) port->set_parameter(par_ptr->parameter_name,
105 par_ptr->parameter_value);
106 } else {
107 // the parameter refers to all ports (*)
108 for (PORT *port = list_head; port != NULL; port = port->list_next)
109 port->set_parameter(par_ptr->parameter_name,
110 par_ptr->parameter_value);
111 }
112 }
113
114 void PORT::set_system_parameters(const char *system_port)
115 {
116 for (port_parameter *par = parameter_head; par != NULL; par = par->next_par)
117 if (par->component_id.id_selector == COMPONENT_ID_SYSTEM &&
118 (par->port_name == NULL || !strcmp(par->port_name, system_port)))
119 set_parameter(par->parameter_name, par->parameter_value);
120 }
121
122 void PORT::add_parameter(const component_id_t& component_id,
123 const char *par_port_name, const char *parameter_name,
124 const char *parameter_value)
125 {
126 port_parameter *new_par = new port_parameter;
127
128 new_par->component_id.id_selector = component_id.id_selector;
129 switch (component_id.id_selector) {
130 case COMPONENT_ID_NAME:
131 new_par->component_id.id_name = mcopystr(component_id.id_name);
132 break;
133 case COMPONENT_ID_COMPREF:
134 new_par->component_id.id_compref = component_id.id_compref;
135 break;
136 default:
137 break;
138 }
139
140 if (par_port_name == NULL) new_par->port_name = NULL;
141 else new_par->port_name = mcopystr(par_port_name);
142 new_par->parameter_name = mcopystr(parameter_name);
143 new_par->parameter_value = mcopystr(parameter_value);
144
145 new_par->next_par = NULL;
146 if (parameter_head == NULL) parameter_head = new_par;
147 if (parameter_tail != NULL) parameter_tail->next_par = new_par;
148 parameter_tail = new_par;
149 }
150
151 void PORT::clear_parameters()
152 {
153 while (parameter_head != NULL) {
154 port_parameter *next_par = parameter_head->next_par;
155 if (parameter_head->component_id.id_selector == COMPONENT_ID_NAME)
156 Free(parameter_head->component_id.id_name);
157 Free(parameter_head->port_name);
158 Free(parameter_head->parameter_name);
159 Free(parameter_head->parameter_value);
160 delete parameter_head;
161 parameter_head = next_par;
162 }
163 }
164
165 void PORT::set_parameters(component component_reference,
166 const char *component_name)
167 {
168 for (port_parameter *par = parameter_head; par != NULL; par = par->next_par)
169 switch (par->component_id.id_selector) {
170 case COMPONENT_ID_NAME:
171 if (component_name != NULL &&
172 !strcmp(par->component_id.id_name, component_name))
173 apply_parameter(par);
174 break;
175 case COMPONENT_ID_COMPREF:
176 if (par->component_id.id_compref == component_reference)
177 apply_parameter(par);
178 break;
179 case COMPONENT_ID_ALL:
180 apply_parameter(par);
181 break;
182 default:
183 break;
184 }
185 }
186
187 enum connection_data_type_enum {
188 CONN_DATA_LAST = 0, CONN_DATA_MESSAGE = 1, CONN_DATA_CALL = 2,
189 CONN_DATA_REPLY = 3, CONN_DATA_EXCEPTION = 4
190 };
191
192 enum connection_state_enum {
193 CONN_IDLE, CONN_LISTENING, CONN_CONNECTED, CONN_LAST_MSG_SENT,
194 CONN_LAST_MSG_RCVD
195 };
196
197 struct port_connection : public Fd_Event_Handler {
198 PORT *owner_port;
199 connection_state_enum connection_state;
200 component remote_component;
201 char *remote_port;
202 transport_type_enum transport_type;
203 union {
204 struct {
205 PORT *port_ptr;
206 } local;
207 struct {
208 int comm_fd;
209 Text_Buf *incoming_buf;
210 } stream;
211 };
212 struct port_connection *list_prev, *list_next;
213 OCTETSTRING sliding_buffer;
214
215 virtual void Handle_Fd_Event(int fd,
216 boolean is_readable, boolean is_writeable, boolean is_error);
217 virtual ~port_connection();
218 virtual void log() const;
219 };
220
221 void port_connection::Handle_Fd_Event(int,
222 boolean is_readable, boolean /*is_writeable*/, boolean /*is_error*/)
223 {
224 // Note event for connection with TRANSPORT_LOCAL transport_type
225 // may not arrive.
226 if (transport_type == TRANSPORT_INET_STREAM
227 || transport_type == TRANSPORT_UNIX_STREAM
228 ) {
229 if (is_readable) {
230 if (connection_state == CONN_LISTENING)
231 owner_port->handle_incoming_connection(this);
232 else owner_port->handle_incoming_data(this);
233 }
234 } else
235 TTCN_error("Internal error: Invalid transport type (%d) in port "
236 "connection between %s and %d:%s.", transport_type,
237 owner_port->get_name(), remote_component, remote_port);
238 }
239
240 void port_connection::log() const
241 {
242 TTCN_Logger::log_event("port connection between ");
243 owner_port->log(); TTCN_Logger::log_event(" and ");
244 TTCN_Logger::log_event(remote_component); TTCN_Logger::log_event(":");
245 TTCN_Logger::log_event("%s", remote_port);
246 }
247
248 port_connection::~port_connection()
249 {
250 if (transport_type == TRANSPORT_INET_STREAM
251 || transport_type == TRANSPORT_UNIX_STREAM
252 ) {
253 if (stream.comm_fd != -1) {
254 TTCN_warning_begin("Internal Error: File descriptor %d not "
255 "closed/removed in ", stream.comm_fd); log();
256 TTCN_warning_end();
257 }
258 }
259 sliding_buffer.clean_up();
260 }
261
262 PORT::PORT(const char *par_port_name)
263 {
264 port_name = par_port_name != NULL ? par_port_name : "<unknown>";
265 is_active = FALSE;
266 is_started = FALSE;
267 is_halted = FALSE;
268 list_prev = NULL;
269 list_next = NULL;
270
271 connection_list_head = NULL;
272 connection_list_tail = NULL;
273 n_system_mappings = 0;
274 system_mappings = NULL;
275 }
276
277 PORT::~PORT()
278 {
279 if (is_active) deactivate_port();
280 }
281
282 void PORT::set_name(const char * name)
283 {
284 if (name == NULL) TTCN_error("Internal error: Setting an "
285 "invalid name for a single element of a port array.");
286 port_name = name;
287 }
288
289 void PORT::log() const
290 {
291 TTCN_Logger::log_event("port %s", port_name);
292 }
293
294 void PORT::activate_port()
295 {
296 if (!is_active) {
297 add_to_list();
298 is_active = TRUE;
299 msg_head_count = 0;
300 msg_tail_count = 0;
301 proc_head_count = 0;
302 proc_tail_count = 0;
303 }
304 }
305
306 void PORT::deactivate_port()
307 {
308 if (is_active) {
309 /* In order to proceed with the deactivation we must ignore the
310 * following errors:
311 * - errors in user code of Test Port (i.e. user_stop, user_unmap)
312 * - failures when sending messages to MC (the link may be down)
313 */
314 boolean is_parallel = !TTCN_Runtime::is_single();
315 // terminate all connections
316 while (connection_list_head != NULL) {
317 TTCN_Logger::log_port_misc(
318 TitanLoggerApi::Port__Misc_reason::removing__unterminated__connection,
319 port_name,
320 connection_list_head->remote_component, connection_list_head->remote_port);
321 if (is_parallel) {
322 try {
323 TTCN_Communication::send_disconnected(port_name,
324 connection_list_head->remote_component,
325 connection_list_head->remote_port);
326 } catch (const TC_Error&) { }
327 }
328 remove_connection(connection_list_head);
329 }
330 // terminate all mappings
331 while (n_system_mappings > 0) {
332 // we must make a copy of the string because unmap() will destroy it
333 char *system_port = mcopystr(system_mappings[0]);
334 TTCN_Logger::log_port_misc(
335 TitanLoggerApi::Port__Misc_reason::removing__unterminated__mapping,
336 port_name, NULL_COMPREF, system_port);
337 try {
338 unmap(system_port);
339 } catch (const TC_Error&) { }
340 if (is_parallel) {
341 try {
342 TTCN_Communication::send_unmapped(port_name, system_port);
343 } catch (const TC_Error&) { }
344 }
345 Free(system_port);
346 }
347 // the previous disconnect/unmap operations may generate incoming events
348 // so we should stop and clear the queue after them
349 if (is_started || is_halted) {
350 try {
351 stop();
352 } catch (const TC_Error&) { }
353 }
354 clear_queue();
355 // deactivate all event handlers
356 Fd_And_Timeout_User::remove_all_fds(this);
357 Fd_And_Timeout_User::set_timer(this, 0.0);
358 // File descriptor events of port connections are removed
359 // in remove_connection
360 remove_from_list();
361 is_active = FALSE;
362 }
363 }
364
365 void PORT::deactivate_all()
366 {
367 while (list_head != NULL) list_head->deactivate_port();
368 }
369
370 void PORT::clear()
371 {
372 if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
373 "be cleared.", port_name);
374 if (!is_started && !is_halted) {
375 TTCN_warning("Performing clear operation on port %s, which is "
376 "already stopped. The operation has no effect.", port_name);
377 }
378 clear_queue();
379 TTCN_Logger::log_port_misc(
380 TitanLoggerApi::Port__Misc_reason::port__was__cleared, port_name);
381 }
382
383 void PORT::all_clear()
384 {
385 for (PORT *port = list_head; port != NULL; port = port->list_next)
386 port->clear();
387 }
388
389 void PORT::start()
390 {
391 if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
392 "be started.", port_name);
393 if (is_started) {
394 TTCN_warning("Performing start operation on port %s, which is "
395 "already started. The operation will clear the incoming queue.",
396 port_name);
397 clear_queue();
398 } else {
399 if (is_halted) {
400 // the queue might contain old messages which has to be discarded
401 clear_queue();
402 is_halted = FALSE;
403 }
404 user_start();
405 is_started = TRUE;
406 }
407 TTCN_Logger::log_port_state(TitanLoggerApi::Port__State_operation::started,
408 port_name);
409 }
410
411 void PORT::all_start()
412 {
413 for (PORT *port = list_head; port != NULL; port = port->list_next)
414 port->start();
415 }
416
417 void PORT::stop()
418 {
419 if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
420 "be stopped.", port_name);
421 if (is_started) {
422 is_started = FALSE;
423 is_halted = FALSE;
424 user_stop();
425 // dropping all messages from the queue because they cannot be
426 // extracted by receiving operations anymore
427 clear_queue();
428 } else if (is_halted) {
429 is_halted = FALSE;
430 clear_queue();
431 } else {
432 TTCN_warning("Performing stop operation on port %s, which is "
433 "already stopped. The operation has no effect.", port_name);
434 }
435 TTCN_Logger::log_port_state(TitanLoggerApi::Port__State_operation::stopped,
436 port_name);
437 }
438
439 void PORT::all_stop()
440 {
441 for (PORT *port = list_head; port != NULL; port = port->list_next)
442 port->stop();
443 }
444
445 void PORT::halt()
446 {
447 if (!is_active) TTCN_error("Internal error: Inactive port %s cannot "
448 "be halted.", port_name);
449 if (is_started) {
450 is_started = FALSE;
451 is_halted = TRUE;
452 user_stop();
453 // keep the messages in the queue
454 } else if (is_halted) {
455 TTCN_warning("Performing halt operation on port %s, which is "
456 "already halted. The operation has no effect.", port_name);
457 } else {
458 TTCN_warning("Performing halt operation on port %s, which is "
459 "already stopped. The operation has no effect.", port_name);
460 }
461 TTCN_Logger::log_port_state(TitanLoggerApi::Port__State_operation::halted,
462 port_name);
463 }
464
465 void PORT::all_halt()
466 {
467 for (PORT *port = list_head; port != NULL; port = port->list_next)
468 port->halt();
469 }
470
471 alt_status PORT::receive(const COMPONENT_template&, COMPONENT *)
472 {
473 TTCN_Logger::log_matching_problem(
474 TitanLoggerApi::MatchingProblemType_reason::no__incoming__types,
475 TitanLoggerApi::MatchingProblemType_operation::receive__,
476 false, false, port_name);
477 return ALT_NO;
478 }
479
480 alt_status PORT::any_receive(const COMPONENT_template& sender_template,
481 COMPONENT *sender_ptr)
482 {
483 if (list_head != NULL) {
484 alt_status ret_val = ALT_NO;
485 for (PORT *port = list_head; port != NULL; port = port->list_next) {
486 switch (port->receive(sender_template, sender_ptr)) {
487 case ALT_YES:
488 return ALT_YES;
489 case ALT_MAYBE:
490 ret_val = ALT_MAYBE;
491 break;
492 case ALT_NO:
493 break;
494 default:
495 TTCN_error("Internal error: Receive operation returned "
496 "unexpected status code on port %s while evaluating "
497 "`any port.receive'.", port->port_name);
498 }
499 }
500 return ret_val;
501 } else {
502 TTCN_Logger::log_matching_problem(
503 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
504 TitanLoggerApi::MatchingProblemType_operation::receive__,
505 true, false);
506 return ALT_NO;
507 }
508 }
509
510 alt_status PORT::check_receive(const COMPONENT_template&, COMPONENT *)
511 {
512 TTCN_Logger::log_matching_problem(
513 TitanLoggerApi::MatchingProblemType_reason::no__incoming__types,
514 TitanLoggerApi::MatchingProblemType_operation::receive__,
515 false, true, port_name);
516 return ALT_NO;
517 }
518
519 alt_status PORT::any_check_receive(const COMPONENT_template& sender_template,
520 COMPONENT *sender_ptr)
521 {
522 if (list_head != NULL) {
523 alt_status ret_val = ALT_NO;
524 for (PORT *port = list_head; port != NULL; port = port->list_next) {
525 switch (port->check_receive(sender_template, sender_ptr)) {
526 case ALT_YES:
527 return ALT_YES;
528 case ALT_MAYBE:
529 ret_val = ALT_MAYBE;
530 break;
531 case ALT_NO:
532 break;
533 default:
534 TTCN_error("Internal error: Check-receive operation returned "
535 "unexpected status code on port %s while evaluating "
536 "`any port.check(receive)'.", port->port_name);
537 }
538 }
539 return ret_val;
540 } else {
541 TTCN_Logger::log_matching_problem(
542 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
543 TitanLoggerApi::MatchingProblemType_operation::receive__,
544 true, true);
545 return ALT_NO;
546 }
547 }
548
549 alt_status PORT::trigger(const COMPONENT_template&, COMPONENT *)
550 {
551 TTCN_Logger::log_matching_problem(
552 TitanLoggerApi::MatchingProblemType_reason::no__incoming__types,
553 TitanLoggerApi::MatchingProblemType_operation::trigger__,
554 false, false, port_name);
555 return ALT_NO;
556 }
557
558 alt_status PORT::any_trigger(const COMPONENT_template& sender_template,
559 COMPONENT *sender_ptr)
560 {
561 if (list_head != NULL) {
562 alt_status ret_val = ALT_NO;
563 for (PORT *port = list_head; port != NULL; port = port->list_next) {
564 switch (port->trigger(sender_template, sender_ptr)) {
565 case ALT_YES:
566 return ALT_YES;
567 case ALT_MAYBE:
568 ret_val = ALT_MAYBE;
569 break;
570 case ALT_NO:
571 break;
572 case ALT_REPEAT:
573 return ALT_REPEAT;
574 default:
575 TTCN_error("Internal error: Trigger operation returned "
576 "unexpected status code on port %s while evaluating "
577 "`any port.trigger'.", port->port_name);
578 }
579 }
580 return ret_val;
581 } else {
582 TTCN_Logger::log_matching_problem(
583 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
584 TitanLoggerApi::MatchingProblemType_operation::trigger__,
585 true, false);
586 return ALT_NO;
587 }
588 }
589
590 alt_status PORT::getcall(const COMPONENT_template&, COMPONENT *)
591 {
592 // ToDo:Unnecessary log matching problem warning removed.
593 // Question: does it unnecessary?
594 // TTCN_Logger::log_matching_problem(
595 // TitanLoggerApi::MatchingProblemType_reason::no__incoming__signatures,
596 // TitanLoggerApi::MatchingProblemType_operation::getcall__,
597 // false, false, port_name);
598 return ALT_NO;
599 }
600
601 alt_status PORT::any_getcall(const COMPONENT_template& sender_template,
602 COMPONENT *sender_ptr)
603 {
604 if (list_head != NULL) {
605 alt_status ret_val = ALT_NO;
606 for (PORT *port = list_head; port != NULL; port = port->list_next) {
607 switch (port->getcall(sender_template, sender_ptr)) {
608 case ALT_YES:
609 return ALT_YES;
610 case ALT_MAYBE:
611 ret_val = ALT_MAYBE;
612 break;
613 case ALT_NO:
614 break;
615 default:
616 TTCN_error("Internal error: Getcall operation returned "
617 "unexpected status code on port %s while evaluating "
618 "`any port.getcall'.", port->port_name);
619 }
620 }
621 return ret_val;
622 } else {
623 TTCN_Logger::log_matching_problem(
624 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
625 TitanLoggerApi::MatchingProblemType_operation::getcall__,
626 true, false);
627 return ALT_NO;
628 }
629 }
630
631 alt_status PORT::check_getcall(const COMPONENT_template&, COMPONENT *)
632 {
633 // ToDo:Unnecessary log matching problem warning removed.
634 // Question: does it unnecessary
635 // TTCN_Logger::log_matching_problem(
636 // TitanLoggerApi::MatchingProblemType_reason::no__incoming__signatures,
637 // TitanLoggerApi::MatchingProblemType_operation::getcall__,
638 // false, false, port_name);
639 return ALT_NO;
640 }
641
642 alt_status PORT::any_check_getcall(const COMPONENT_template& sender_template,
643 COMPONENT *sender_ptr)
644 {
645 if (list_head != NULL) {
646 alt_status ret_val = ALT_NO;
647 for (PORT *port = list_head; port != NULL; port = port->list_next) {
648 switch (port->check_getcall(sender_template, sender_ptr)) {
649 case ALT_YES:
650 return ALT_YES;
651 case ALT_MAYBE:
652 ret_val = ALT_MAYBE;
653 break;
654 case ALT_NO:
655 break;
656 default:
657 TTCN_error("Internal error: Check-getcall operation returned "
658 "unexpected status code on port %s while evaluating "
659 "`any port.check(getcall)'.", port->port_name);
660 }
661 }
662 return ret_val;
663 } else {
664 TTCN_Logger::log_matching_problem(
665 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
666 TitanLoggerApi::MatchingProblemType_operation::getcall__,
667 true, true);
668 return ALT_NO;
669 }
670 }
671
672 alt_status PORT::getreply(const COMPONENT_template&, COMPONENT *)
673 {
674 // ToDo:Unnecessary log matching problem warning removed.
675 // Question: does it unnecessary
676 // TTCN_Logger::log_matching_problem(
677 // TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures,
678 // TitanLoggerApi::MatchingProblemType_operation::getreply__,
679 // false, false, port_name);
680 return ALT_NO;
681 }
682
683 alt_status PORT::any_getreply(const COMPONENT_template& sender_template,
684 COMPONENT *sender_ptr)
685 {
686 if (list_head != NULL) {
687 alt_status ret_val = ALT_NO;
688 for (PORT *port = list_head; port != NULL; port = port->list_next) {
689 switch (port->getreply(sender_template, sender_ptr)) {
690 case ALT_YES:
691 return ALT_YES;
692 case ALT_MAYBE:
693 ret_val = ALT_MAYBE;
694 break;
695 case ALT_NO:
696 break;
697 default:
698 TTCN_error("Internal error: Getreply operation returned "
699 "unexpected status code on port %s while evaluating "
700 "`any port.getreply'.", port->port_name);
701 }
702 }
703 return ret_val;
704 } else {
705 TTCN_Logger::log_matching_problem(
706 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
707 TitanLoggerApi::MatchingProblemType_operation::getreply__,
708 true, false);
709 return ALT_NO;
710 }
711 }
712
713 alt_status PORT::check_getreply(const COMPONENT_template&, COMPONENT *)
714 {
715 // ToDo:Unnecessary log matching problem warning removed.
716 // Question: does it unnecessary
717 // TTCN_Logger::log_matching_problem(
718 // TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures,
719 // TitanLoggerApi::MatchingProblemType_operation::getreply__,
720 // false, true, port_name);
721 return ALT_NO;
722 }
723
724 alt_status PORT::any_check_getreply(const COMPONENT_template& sender_template,
725 COMPONENT *sender_ptr)
726 {
727 if (list_head != NULL) {
728 alt_status ret_val = ALT_NO;
729 for (PORT *port = list_head; port != NULL; port = port->list_next) {
730 switch (port->check_getreply(sender_template, sender_ptr)) {
731 case ALT_YES:
732 return ALT_YES;
733 case ALT_MAYBE:
734 ret_val = ALT_MAYBE;
735 break;
736 case ALT_NO:
737 break;
738 default:
739 TTCN_error("Internal error: Check-getreply operation returned "
740 "unexpected status code on port %s while evaluating "
741 "`any port.check(getreply)'.", port->port_name);
742 }
743 }
744 return ret_val;
745 } else {
746 TTCN_Logger::log_matching_problem(
747 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
748 TitanLoggerApi::MatchingProblemType_operation::getreply__,
749 true, true);
750 return ALT_NO;
751 }
752 }
753
754 alt_status PORT::get_exception(const COMPONENT_template&, COMPONENT *)
755 {
756 // ToDo:Unnecessary log matching problem warning removed.
757 // Question: does it unnecessary
758 // TTCN_Logger::log_matching_problem(
759 // TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures__that__support__exceptions,
760 // TitanLoggerApi::MatchingProblemType_operation::catch__,
761 // false, false, port_name);
762 return ALT_NO;
763 }
764
765 alt_status PORT::any_catch(const COMPONENT_template& sender_template,
766 COMPONENT *sender_ptr)
767 {
768 if (list_head != NULL) {
769 alt_status ret_val = ALT_NO;
770 for (PORT *port = list_head; port != NULL; port = port->list_next) {
771 switch (port->get_exception(sender_template, sender_ptr)) {
772 case ALT_YES:
773 return ALT_YES;
774 case ALT_MAYBE:
775 ret_val = ALT_MAYBE;
776 break;
777 case ALT_NO:
778 break;
779 default:
780 TTCN_error("Internal error: Catch operation returned "
781 "unexpected status code on port %s while evaluating "
782 "`any port.catch'.", port->port_name);
783 }
784 }
785 return ret_val;
786 } else {
787 TTCN_Logger::log_matching_problem(
788 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
789 TitanLoggerApi::MatchingProblemType_operation::catch__,
790 true, false);
791 return ALT_NO;
792 }
793 }
794
795 alt_status PORT::check_catch(const COMPONENT_template& ,
796 COMPONENT *)
797 {
798 // ToDo:Unnecessary log matching problem warning removed.
799 // Question: does it unnecessary
800 // TTCN_Logger::log_matching_problem(
801 // TitanLoggerApi::MatchingProblemType_reason::no__outgoing__blocking__signatures__that__support__exceptions,
802 // TitanLoggerApi::MatchingProblemType_operation::catch__,
803 // false, true, port_name);
804 return ALT_NO;
805 }
806
807 alt_status PORT::any_check_catch(const COMPONENT_template& sender_template,
808 COMPONENT *sender_ptr)
809 {
810 if (list_head != NULL) {
811 alt_status ret_val = ALT_NO;
812 for (PORT *port = list_head; port != NULL; port = port->list_next) {
813 switch (port->check_catch(sender_template, sender_ptr)) {
814 case ALT_YES:
815 return ALT_YES;
816 case ALT_MAYBE:
817 ret_val = ALT_MAYBE;
818 break;
819 case ALT_NO:
820 break;
821 default:
822 TTCN_error("Internal error: Check-catch operation returned "
823 "unexpected status code on port %s while evaluating "
824 "`any port.check(catch)'.", port->port_name);
825 }
826 }
827 return ret_val;
828 } else {
829 TTCN_Logger::log_matching_problem(
830 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
831 TitanLoggerApi::MatchingProblemType_operation::catch__,
832 true, true);
833 return ALT_NO;
834 }
835 }
836
837 alt_status PORT::check(const COMPONENT_template& sender_template,
838 COMPONENT *sender_ptr)
839 {
840 alt_status ret_val = ALT_NO;
841 // the procedure-based queue must have the higher priority
842 switch (check_getcall(sender_template, sender_ptr)) {
843 case ALT_YES:
844 return ALT_YES;
845 case ALT_MAYBE:
846 ret_val = ALT_MAYBE;
847 break;
848 case ALT_NO:
849 break;
850 default:
851 TTCN_error("Internal error: Check-getcall operation returned "
852 "unexpected status code on port %s.", port_name);
853 }
854 if (ret_val != ALT_MAYBE) {
855 // don't try getreply if the procedure-based queue is empty
856 // (i.e. check_getcall() returned ALT_MAYBE)
857 switch (check_getreply(sender_template, sender_ptr)) {
858 case ALT_YES:
859 return ALT_YES;
860 case ALT_MAYBE:
861 ret_val = ALT_MAYBE;
862 break;
863 case ALT_NO:
864 break;
865 default:
866 TTCN_error("Internal error: Check-getreply operation returned "
867 "unexpected status code on port %s.", port_name);
868 }
869 }
870 if (ret_val != ALT_MAYBE) {
871 // don't try catch if the procedure-based queue is empty
872 // (i.e. check_getcall() or check_getreply() returned ALT_MAYBE)
873 switch (check_catch(sender_template, sender_ptr)) {
874 case ALT_YES:
875 return ALT_YES;
876 case ALT_MAYBE:
877 ret_val = ALT_MAYBE;
878 break;
879 case ALT_NO:
880 break;
881 default:
882 TTCN_error("Internal error: Check-catch operation returned "
883 "unexpected status code on port %s.", port_name);
884 }
885 }
886 switch (check_receive(sender_template, sender_ptr)) {
887 case ALT_YES:
888 return ALT_YES;
889 case ALT_MAYBE:
890 ret_val = ALT_MAYBE;
891 break;
892 case ALT_NO:
893 break;
894 default:
895 TTCN_error("Internal error: Check-receive operation returned "
896 "unexpected status code on port %s.", port_name);
897 }
898 return ret_val;
899 }
900
901 alt_status PORT::any_check(const COMPONENT_template& sender_template,
902 COMPONENT *sender_ptr)
903 {
904 if (list_head != NULL) {
905 alt_status ret_val = ALT_NO;
906 for (PORT *port = list_head; port != NULL; port = port->list_next) {
907 switch (port->check(sender_template, sender_ptr)) {
908 case ALT_YES:
909 return ALT_YES;
910 case ALT_MAYBE:
911 ret_val = ALT_MAYBE;
912 break;
913 case ALT_NO:
914 break;
915 default:
916 TTCN_error("Internal error: Check operation returned "
917 "unexpected status code on port %s while evaluating "
918 "`any port.check'.", port->port_name);
919 }
920 }
921 return ret_val;
922 } else {
923 TTCN_Logger::log_matching_problem(
924 TitanLoggerApi::MatchingProblemType_reason::component__has__no__ports,
925 TitanLoggerApi::MatchingProblemType_operation::check__,
926 true, false);
927 return ALT_NO;
928 }
929 }
930
931 void PORT::set_parameter(const char *parameter_name, const char *)
932 {
933 TTCN_warning("Test port parameter %s is not supported on port %s.",
934 parameter_name, port_name);
935 }
936
937 void PORT::append_to_msg_queue(msg_queue_item_base* new_item)
938 {
939 new_item->next_item = NULL;
940 if (msg_queue_tail == NULL) msg_queue_head = new_item;
941 else msg_queue_tail->next_item = new_item;
942 msg_queue_tail = new_item;
943 }
944
945 void PORT::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable,
946 boolean is_error)
947 {
948 // The port intends to use the finer granularity event handler functions
949 if (is_error) {
950 Handle_Fd_Event_Error(fd);
951 if (!is_writable && !is_readable) return;
952 fd_event_type_enum event = Fd_And_Timeout_User::getCurReceivedEvent();
953 if ((event & FD_EVENT_WR) == 0) is_writable = FALSE;
954 if ((event & FD_EVENT_RD) == 0) is_readable = FALSE;
955 }
956 if (is_writable) {
957 Handle_Fd_Event_Writable(fd);
958 if (!is_readable) return;
959 if ((Fd_And_Timeout_User::getCurReceivedEvent() & FD_EVENT_RD) == 0)
960 return;
961 }
962 if (is_readable)
963 Handle_Fd_Event_Readable(fd);
964 }
965
966 void PORT::Handle_Fd_Event_Error(int)
967 {
968 // Silently ignore
969 // A port need not wait for error events
970 // Note: error events always cause event handler invocation
971 }
972
973 void PORT::Handle_Fd_Event_Writable(int)
974 {
975 TTCN_error("There is no Handle_Fd_Event_Writable member function "
976 "implemented in port %s. "
977 "This method or the Handle_Fd_Event method has to be implemented in "
978 "the port if the port waits for any file descriptor to be writable - "
979 "unless the port uses Install_Handler to specify the file descriptor "
980 "and timeout events for which the port waits.", port_name);
981 }
982
983 void PORT::Handle_Fd_Event_Readable(int)
984 {
985 TTCN_error("There is no Handle_Fd_Event_Readable member function "
986 "implemented in port %s. "
987 "This method or the Handle_Fd_Event method has to be implemented in "
988 "the port if the port waits for any file descriptor to be readable - "
989 "unless the port uses Install_Handler to specify the file descriptor "
990 "and timeout events for which the port waits.", port_name);
991 }
992
993 void PORT::Handle_Timeout(double /*time_since_last_call*/)
994 {
995 TTCN_error("There is no Handle_Timeout member function implemented in "
996 "port %s. "
997 "This method has to be implemented in the port if the port waits for "
998 "timeouts unless the port uses Install_Handler to specify the timeout.",
999 port_name);
1000 }
1001
1002 void PORT::Event_Handler(const fd_set * /*read_fds*/, const fd_set * /*write_fds*/,
1003 const fd_set * /*error_fds*/, double /*time_since_last_call*/)
1004 {
1005 TTCN_error("There is no Event_Handler implemented in port %s. "
1006 "Event_Handler has to be implemented in the port if "
1007 "Install_Handler is used to specify the file descriptor and timeout "
1008 "events for which the port waits.", port_name);
1009 }
1010
1011 void PORT::Handler_Add_Fd(int fd, Fd_Event_Type event_mask)
1012 {
1013 Fd_And_Timeout_User::add_fd(fd, this,
1014 static_cast<fd_event_type_enum>(
1015 static_cast<int>(event_mask)));
1016 }
1017
1018 void PORT::Handler_Add_Fd_Read(int fd)
1019 {
1020 Fd_And_Timeout_User::add_fd(fd, this, FD_EVENT_RD);
1021 }
1022
1023 void PORT::Handler_Add_Fd_Write(int fd)
1024 {
1025 Fd_And_Timeout_User::add_fd(fd, this, FD_EVENT_WR);
1026 }
1027
1028 void PORT::Handler_Remove_Fd(int fd, Fd_Event_Type event_mask)
1029 {
1030 Fd_And_Timeout_User::remove_fd(fd, this,
1031 static_cast<fd_event_type_enum>(
1032 static_cast<int>(event_mask)));
1033 }
1034
1035 void PORT::Handler_Remove_Fd_Read(int fd)
1036 {
1037 Fd_And_Timeout_User::remove_fd(fd, this, FD_EVENT_RD);
1038 }
1039
1040 void PORT::Handler_Remove_Fd_Write(int fd)
1041 {
1042 Fd_And_Timeout_User::remove_fd(fd, this, FD_EVENT_WR);
1043 }
1044
1045 void PORT::Handler_Set_Timer(double call_interval, boolean is_timeout,
1046 boolean call_anyway, boolean is_periodic)
1047 {
1048 Fd_And_Timeout_User::set_timer(this, call_interval, is_timeout, call_anyway,
1049 is_periodic);
1050 }
1051
1052 void PORT::Install_Handler(const fd_set *read_fds, const fd_set *write_fds,
1053 const fd_set *error_fds, double call_interval)
1054 {
1055 if (!is_active) TTCN_error("Event handler cannot be installed for "
1056 "inactive port %s.", port_name);
1057
1058 if ((long) FdMap::getFdLimit() > (long) FD_SETSIZE) {
1059 static bool once = true;
1060 if (once) {
1061 TTCN_warning("The maximum number of open file descriptors (%i)"
1062 " is greater than FD_SETSIZE (%li)."
1063 " Ensure that Test Ports using Install_Handler do not try to"
1064 " wait for events of file descriptors with values greater than"
1065 " FD_SETSIZE (%li)."
1066 " (Current caller of Install_Handler is \"%s\")",
1067 FdMap::getFdLimit(), (long) FD_SETSIZE, (long) FD_SETSIZE,
1068 port_name);
1069 }
1070 once = false;
1071 }
1072
1073 Fd_And_Timeout_User::set_fds_with_fd_sets(this, read_fds, write_fds,
1074 error_fds);
1075 Fd_And_Timeout_User::set_timer(this, call_interval);
1076 }
1077
1078 void PORT::Uninstall_Handler()
1079 {
1080 Fd_And_Timeout_User::remove_all_fds(this);
1081 Fd_And_Timeout_User::set_timer(this, 0.0);
1082 }
1083
1084 void PORT::user_map(const char *)
1085 {
1086
1087 }
1088
1089 void PORT::user_unmap(const char *)
1090 {
1091
1092 }
1093
1094 void PORT::user_start()
1095 {
1096
1097 }
1098
1099 void PORT::user_stop()
1100 {
1101
1102 }
1103
1104 void PORT::clear_queue()
1105 {
1106
1107 }
1108
1109 component PORT::get_default_destination()
1110 {
1111 if (connection_list_head != NULL) {
1112 if (n_system_mappings > 0) TTCN_error("Port %s has both connection(s) "
1113 "and mapping(s). Message can be sent on it only with explicit "
1114 "addressing.", port_name);
1115 else if (connection_list_head->list_next != NULL) TTCN_error("Port %s "
1116 "has more than one active connections. Message can be sent on it "
1117 "only with explicit addressing.", port_name);
1118 return connection_list_head->remote_component;
1119 } else {
1120 if (n_system_mappings > 1) {
1121 TTCN_error("Port %s has more than one mappings. Message cannot "
1122 "be sent on it to system.", port_name);
1123 } else if (n_system_mappings < 1) {
1124 TTCN_error("Port %s has neither connections nor mappings. "
1125 "Message cannot be sent on it.", port_name);
1126 }
1127 return SYSTEM_COMPREF;
1128 }
1129 }
1130
1131 void PORT::prepare_message(Text_Buf& outgoing_buf, const char *message_type)
1132 {
1133 outgoing_buf.push_int(CONN_DATA_MESSAGE);
1134 outgoing_buf.push_string(message_type);
1135 }
1136
1137 void PORT::prepare_call(Text_Buf& outgoing_buf, const char *signature_name)
1138 {
1139 outgoing_buf.push_int(CONN_DATA_CALL);
1140 outgoing_buf.push_string(signature_name);
1141 }
1142
1143 void PORT::prepare_reply(Text_Buf& outgoing_buf, const char *signature_name)
1144 {
1145 outgoing_buf.push_int(CONN_DATA_REPLY);
1146 outgoing_buf.push_string(signature_name);
1147 }
1148
1149 void PORT::prepare_exception(Text_Buf& outgoing_buf, const char *signature_name)
1150 {
1151 outgoing_buf.push_int(CONN_DATA_EXCEPTION);
1152 outgoing_buf.push_string(signature_name);
1153 }
1154
1155 void PORT::send_data(Text_Buf &outgoing_buf,
1156 const COMPONENT& destination_component)
1157 {
1158 if (!destination_component.is_bound()) TTCN_error("Internal error: "
1159 "The destination component reference is unbound when sending data on "
1160 "port %s.", port_name);
1161 component destination_compref = (component)destination_component;
1162 boolean is_unique;
1163 port_connection *conn_ptr =
1164 lookup_connection_to_compref(destination_compref, &is_unique);
1165 if (conn_ptr == NULL)
1166 TTCN_error("Data cannot be sent on port %s to component %d "
1167 "because there is no connection towards component %d.", port_name,
1168 destination_compref, destination_compref);
1169 else if (!is_unique)
1170 TTCN_error("Data cannot be sent on port %s to component %d "
1171 "because there are more than one connections towards component "
1172 "%d.", port_name, destination_compref, destination_compref);
1173 else if (conn_ptr->connection_state != CONN_CONNECTED)
1174 TTCN_error("Data cannot be sent on port %s to component %d "
1175 "because the connection is not in active state.",
1176 port_name, destination_compref);
1177 switch (conn_ptr->transport_type) {
1178 case TRANSPORT_LOCAL:
1179 send_data_local(conn_ptr, outgoing_buf);
1180 break;
1181 case TRANSPORT_INET_STREAM:
1182 case TRANSPORT_UNIX_STREAM:
1183 send_data_stream(conn_ptr, outgoing_buf, FALSE);
1184 break;
1185 default:
1186 TTCN_error("Internal error: Invalid transport type (%d) in port "
1187 "connection between %s and %d:%s.", conn_ptr->transport_type,
1188 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
1189 }
1190 }
1191
1192 void PORT::process_data(port_connection *conn_ptr, Text_Buf& incoming_buf)
1193 {
1194 connection_data_type_enum conn_data_type =
1195 (connection_data_type_enum)incoming_buf.pull_int().get_val();
1196 if (conn_data_type != CONN_DATA_LAST) {
1197 switch (conn_ptr->connection_state) {
1198 case CONN_CONNECTED:
1199 case CONN_LAST_MSG_SENT:
1200 break;
1201 case CONN_LAST_MSG_RCVD:
1202 case CONN_IDLE:
1203 TTCN_warning("Data arrived after the indication of connection "
1204 "termination on port %s from %d:%s. Data is ignored.",
1205 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
1206 return;
1207 default:
1208 TTCN_error("Internal error: Connection of port %s with %d:%s has "
1209 "invalid state (%d).", port_name, conn_ptr->remote_component,
1210 conn_ptr->remote_port, conn_ptr->connection_state);
1211 }
1212 char *message_type = incoming_buf.pull_string();
1213 try {
1214 switch (conn_data_type) {
1215 case CONN_DATA_MESSAGE:
1216 if (!process_message(message_type, incoming_buf,
1217 conn_ptr->remote_component, conn_ptr->sliding_buffer)) {
1218 TTCN_error("Port %s does not support incoming message "
1219 "type %s, which has arrived on the connection from "
1220 "%d:%s.", port_name, message_type,
1221 conn_ptr->remote_component, conn_ptr->remote_port);
1222 }
1223 break;
1224 case CONN_DATA_CALL:
1225 if (!process_call(message_type, incoming_buf,
1226 conn_ptr->remote_component)) {
1227 TTCN_error("Port %s does not support incoming call of "
1228 "signature %s, which has arrived on the connection "
1229 "from %d:%s.", port_name, message_type,
1230 conn_ptr->remote_component, conn_ptr->remote_port);
1231 }
1232 break;
1233 case CONN_DATA_REPLY:
1234 if (!process_reply(message_type, incoming_buf,
1235 conn_ptr->remote_component)) {
1236 TTCN_error("Port %s does not support incoming reply of "
1237 "signature %s, which has arrived on the connection "
1238 "from %d:%s.", port_name, message_type,
1239 conn_ptr->remote_component, conn_ptr->remote_port);
1240 }
1241 break;
1242 case CONN_DATA_EXCEPTION:
1243 if (!process_exception(message_type, incoming_buf,
1244 conn_ptr->remote_component)) {
1245 TTCN_error("Port %s does not support incoming exception "
1246 "of signature %s, which has arrived on the connection "
1247 "from %d:%s.", port_name, message_type,
1248 conn_ptr->remote_component, conn_ptr->remote_port);
1249 }
1250 break;
1251 default:
1252 TTCN_error("Internal error: Data with invalid selector (%d) "
1253 "was received on port %s from %d:%s.", conn_data_type,
1254 port_name, conn_ptr->remote_component,
1255 conn_ptr->remote_port);
1256 }
1257 } catch (...) {
1258 // avoid memory leak
1259 delete [] message_type;
1260 throw;
1261 }
1262 delete [] message_type;
1263 } else process_last_message(conn_ptr);
1264 }
1265
1266 boolean PORT::process_message(const char *, Text_Buf&, component, OCTETSTRING&)
1267 {
1268 return FALSE;
1269 }
1270
1271 boolean PORT::process_call(const char *, Text_Buf&, component)
1272 {
1273 return FALSE;
1274 }
1275
1276 boolean PORT::process_reply(const char *, Text_Buf&, component)
1277 {
1278 return FALSE;
1279 }
1280
1281 boolean PORT::process_exception(const char *, Text_Buf&, component)
1282 {
1283 return FALSE;
1284 }
1285
1286 port_connection *PORT::add_connection(component remote_component,
1287 const char *remote_port, transport_type_enum transport_type)
1288 {
1289 port_connection *conn_ptr;
1290 for (conn_ptr = connection_list_head; conn_ptr != NULL;
1291 conn_ptr = conn_ptr->list_next) {
1292 if (conn_ptr->remote_component == remote_component) {
1293 int ret_val = strcmp(conn_ptr->remote_port, remote_port);
1294 if (ret_val == 0) return conn_ptr;
1295 else if (ret_val > 0) break;
1296 } else if (conn_ptr->remote_component > remote_component) break;
1297 }
1298
1299 port_connection *new_conn = new port_connection;
1300 new_conn->owner_port = this;
1301
1302 new_conn->connection_state = CONN_IDLE;
1303 new_conn->remote_component = remote_component;
1304 new_conn->remote_port = mcopystr(remote_port);
1305 new_conn->transport_type = transport_type;
1306 new_conn->sliding_buffer = OCTETSTRING(0, 0);
1307 switch (transport_type) {
1308 case TRANSPORT_LOCAL:
1309 new_conn->local.port_ptr = NULL;
1310 break;
1311 case TRANSPORT_INET_STREAM:
1312 case TRANSPORT_UNIX_STREAM:
1313 new_conn->stream.comm_fd = -1;
1314 new_conn->stream.incoming_buf = NULL;
1315 break;
1316 default:
1317 delete new_conn;
1318 TTCN_error("Internal error: PORT::add_connection(): invalid transport "
1319 "type (%d).", transport_type);
1320 }
1321
1322 new_conn->list_next = conn_ptr;
1323 if (conn_ptr != NULL) {
1324 // new_conn will be inserted before conn_ptr in the ordered list
1325 new_conn->list_prev = conn_ptr->list_prev;
1326 conn_ptr->list_prev = new_conn;
1327 if (new_conn->list_prev != NULL)
1328 new_conn->list_prev->list_next = new_conn;
1329 } else {
1330 // new_conn will be inserted to the end of the list
1331 new_conn->list_prev = connection_list_tail;
1332 if (connection_list_tail != NULL)
1333 connection_list_tail->list_next = new_conn;
1334 connection_list_tail = new_conn;
1335 }
1336 if (conn_ptr == connection_list_head) connection_list_head = new_conn;
1337
1338 return new_conn;
1339 }
1340
1341 void PORT::remove_connection(port_connection *conn_ptr)
1342 {
1343 Free(conn_ptr->remote_port);
1344
1345 switch (conn_ptr->transport_type) {
1346 case TRANSPORT_LOCAL:
1347 break;
1348 case TRANSPORT_INET_STREAM:
1349 case TRANSPORT_UNIX_STREAM:
1350 if (conn_ptr->stream.comm_fd >= 0) {
1351 Fd_And_Timeout_User::remove_fd(conn_ptr->stream.comm_fd, conn_ptr,
1352 FD_EVENT_RD);
1353 if (conn_ptr->connection_state == CONN_LISTENING &&
1354 conn_ptr->transport_type == TRANSPORT_UNIX_STREAM)
1355 unlink_unix_pathname(conn_ptr->stream.comm_fd);
1356 close(conn_ptr->stream.comm_fd);
1357 conn_ptr->stream.comm_fd = -1;
1358 }
1359 delete conn_ptr->stream.incoming_buf;
1360 break;
1361 default:
1362 TTCN_error("Internal error: PORT::remove_connection(): invalid "
1363 "transport type.");
1364 }
1365
1366 if (conn_ptr->list_prev != NULL)
1367 conn_ptr->list_prev->list_next = conn_ptr->list_next;
1368 else if (connection_list_head == conn_ptr)
1369 connection_list_head = conn_ptr->list_next;
1370 if (conn_ptr->list_next != NULL)
1371 conn_ptr->list_next->list_prev = conn_ptr->list_prev;
1372 else if (connection_list_tail == conn_ptr)
1373 connection_list_tail = conn_ptr->list_prev;
1374
1375 delete conn_ptr;
1376 }
1377
1378 port_connection *PORT::lookup_connection_to_compref(
1379 component remote_component, boolean *is_unique)
1380 {
1381 for (port_connection *conn = connection_list_head; conn != NULL;
1382 conn = conn->list_next) {
1383 if (conn->remote_component == remote_component) {
1384 if (is_unique != NULL) {
1385 port_connection *nxt = conn->list_next;
1386 if (nxt != NULL && nxt->remote_component == remote_component)
1387 *is_unique = FALSE;
1388 else *is_unique = TRUE;
1389 }
1390 return conn;
1391 } else if (conn->remote_component > remote_component) break;
1392 }
1393 return NULL;
1394 }
1395
1396 port_connection *PORT::lookup_connection(component remote_component,
1397 const char *remote_port)
1398 {
1399 for (port_connection *conn = connection_list_head; conn != NULL;
1400 conn = conn->list_next) {
1401 if (conn->remote_component == remote_component) {
1402 int ret_val = strcmp(conn->remote_port, remote_port);
1403 if (ret_val == 0) return conn;
1404 else if (ret_val > 0) break;
1405 } else if (conn->remote_component > remote_component) break;
1406 }
1407 return NULL;
1408 }
1409
1410 void PORT::add_local_connection(PORT *other_endpoint)
1411 {
1412 port_connection *conn_ptr = add_connection(self, other_endpoint->port_name,
1413 TRANSPORT_LOCAL);
1414 conn_ptr->connection_state = CONN_CONNECTED;
1415 conn_ptr->local.port_ptr = other_endpoint;
1416 TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::local__connection__established,
1417 port_name, NULL_COMPREF, other_endpoint->port_name);
1418 }
1419
1420 void PORT::remove_local_connection(port_connection *conn_ptr)
1421 {
1422 if (conn_ptr->transport_type != TRANSPORT_LOCAL)
1423 TTCN_error("Internal error: The transport type used by the connection "
1424 "between port %s and %d:%s is not LOCAL.", port_name,
1425 conn_ptr->remote_component, conn_ptr->remote_port);
1426 PORT *other_endpoint = conn_ptr->local.port_ptr;
1427 remove_connection(conn_ptr);
1428 TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::local__connection__terminated,
1429 port_name, NULL_COMPREF, other_endpoint->port_name);
1430 }
1431
1432 unsigned int PORT::get_connection_hash(component local_component,
1433 const char *local_port, component remote_component, const char *remote_port)
1434 {
1435 const size_t N = sizeof(unsigned int);
1436 unsigned char hash_buffer[N];
1437
1438 // fill the buffer with an initial pattern
1439 for (size_t i = 0; i < N; i++) hash_buffer[i] = i % 2 ? 0x55 : 0xAA;
1440
1441 // add the PID of the current process to the buffer
1442 pid_t pid = getpid();
1443 for (size_t i = 0; i < sizeof(pid); i++)
1444 hash_buffer[i % N] ^= (pid >> (8 * i)) & 0xFF;
1445
1446 // add the local and remote component reference and port name to the buffer
1447 for (size_t i = 0; i < sizeof(local_component); i++)
1448 hash_buffer[(N - 1) - i % N] ^= (local_component >> (8 * i)) & 0xFF;
1449 for (size_t i = 0; local_port[i] != '\0'; i++)
1450 hash_buffer[(N - 1) - i % N] ^= local_port[i];
1451 for (size_t i = 0; i < sizeof(remote_component); i++)
1452 hash_buffer[i % N] ^= (remote_component >> (8 * i)) & 0xFF;
1453 for (size_t i = 0; remote_port[i] != '\0'; i++)
1454 hash_buffer[i % N] ^= remote_port[i];
1455
1456 // convert the buffer to an integer value
1457 unsigned int ret_val = 0;
1458 for (size_t i = 0; i < N; i++)
1459 ret_val = (ret_val << 8) | hash_buffer[i];
1460 return ret_val;
1461 }
1462
1463 void PORT::unlink_unix_pathname(int socket_fd)
1464 {
1465 struct sockaddr_un local_addr;
1466 // querying the local pathname used by socket_fd
1467 socklen_type addr_len = sizeof(local_addr);
1468 if (getsockname(socket_fd, (struct sockaddr*)&local_addr, &addr_len)) {
1469 TTCN_warning_begin("System call getsockname() failed on UNIX socket "
1470 "file descriptor %d.", socket_fd);
1471 TTCN_Logger::OS_error();
1472 TTCN_Logger::log_event_str(" The associated socket file will not be "
1473 "removed from the file system.");
1474 TTCN_warning_end();
1475 } else if (local_addr.sun_family != AF_UNIX) {
1476 TTCN_warning("System call getsockname() returned invalid address "
1477 "family for UNIX socket file descriptor %d. The associated socket "
1478 "file will not be removed from the file system.", socket_fd);
1479 } else if (unlink(local_addr.sun_path)) {
1480 if (errno != ENOENT) {
1481 TTCN_warning_begin("System call unlink() failed when trying to "
1482 "remove UNIX socket file %s.", local_addr.sun_path);
1483 TTCN_Logger::OS_error();
1484 TTCN_Logger::log_event_str(" The file will remain in the file "
1485 "system.");
1486 TTCN_warning_end();
1487 } else errno = 0;
1488 }
1489 }
1490
1491 void PORT::connect_listen_inet_stream(component remote_component,
1492 const char *remote_port)
1493 {
1494 // creating the TCP server socket
1495 int server_fd = NetworkHandler::socket(TTCN_Communication::get_network_family());
1496 if (server_fd < 0) {
1497 TTCN_Communication::send_connect_error(port_name, remote_component,
1498 remote_port, "Creation of the TCP server socket failed. (%s)",
1499 strerror(errno));
1500 errno = 0;
1501 return;
1502 }
1503
1504 // binding the socket to an ephemeral TCP port
1505 // using the same local IP address as the control connection to MC
1506 IPAddress *local_addr = IPAddress::create_addr(TTCN_Communication::get_network_family());
1507 *local_addr = *TTCN_Communication::get_local_address();
1508 local_addr->set_port(0);
1509 if (bind(server_fd, local_addr->get_addr(), local_addr->get_addr_len())) {
1510 close(server_fd);
1511 TTCN_Communication::send_connect_error(port_name, remote_component,
1512 remote_port, "Binding of server socket to an ephemeral TCP port "
1513 "failed. (%s)", strerror(errno));
1514 errno = 0;
1515 delete local_addr;
1516 return;
1517 }
1518
1519 // zero backlog is enough since we are waiting for only one client
1520 if (listen(server_fd, 0)) {
1521 close(server_fd);
1522 TTCN_Communication::send_connect_error(port_name, remote_component,
1523 remote_port, "Listening on an ephemeral TCP port failed. (%s)",
1524 strerror(errno));
1525 errno = 0;
1526 delete local_addr;
1527 return;
1528 }
1529
1530 // querying the IP address and port number used by the TCP server
1531 if (local_addr->getsockname(server_fd)) {
1532 close(server_fd);
1533 TTCN_Communication::send_connect_error(port_name, remote_component,
1534 remote_port, "System call getsockname() failed on the TCP server "
1535 "socket. (%s)", strerror(errno));
1536 errno = 0;
1537 delete local_addr;
1538 return;
1539 }
1540
1541 if (!TTCN_Communication::set_close_on_exec(server_fd)) {
1542 close(server_fd);
1543 TTCN_Communication::send_connect_error(port_name, remote_component,
1544 remote_port, "Setting the close-on-exec flag failed on the TCP "
1545 "server socket.");
1546 delete local_addr;
1547 return;
1548 }
1549
1550 port_connection *new_connection = add_connection(remote_component,
1551 remote_port, TRANSPORT_INET_STREAM);
1552 new_connection->connection_state = CONN_LISTENING;
1553 new_connection->stream.comm_fd = server_fd;
1554 Fd_And_Timeout_User::add_fd(server_fd, new_connection, FD_EVENT_RD);
1555
1556 TTCN_Communication::send_connect_listen_ack_inet_stream(port_name,
1557 remote_component, remote_port, local_addr);
1558
1559 TTCN_Logger::log_port_misc(
1560 TitanLoggerApi::Port__Misc_reason::port__is__waiting__for__connection__tcp,
1561 port_name, remote_component, remote_port);
1562 delete local_addr;
1563 }
1564
1565 void PORT::connect_listen_unix_stream(component remote_component,
1566 const char *remote_port)
1567 {
1568 // creating the UNIX server socket
1569 int server_fd = socket(PF_UNIX, SOCK_STREAM, 0);
1570 if (server_fd < 0) {
1571 TTCN_Communication::send_connect_error(port_name, remote_component,
1572 remote_port, "Creation of the UNIX server socket failed. (%s)",
1573 strerror(errno));
1574 errno = 0;
1575 return;
1576 }
1577
1578 // binding the socket to a temporary file
1579 struct sockaddr_un local_addr;
1580 // the file name is constructed using a hash function
1581 unsigned int hash_value =
1582 get_connection_hash(self, port_name, remote_component, remote_port);
1583 for (unsigned int i = 1; ; i++) {
1584 memset(&local_addr, 0, sizeof(local_addr));
1585 local_addr.sun_family = AF_UNIX;
1586 snprintf(local_addr.sun_path, sizeof(local_addr.sun_path),
1587 "/tmp/ttcn3-portconn-%x", hash_value);
1588 if (bind(server_fd, (struct sockaddr*)&local_addr, sizeof(local_addr))
1589 == 0) {
1590 // the operation was successful, jump out of the loop
1591 break;
1592 } else if (errno == EADDRINUSE) {
1593 // the temporary file name is already used by someone else
1594 errno = 0;
1595 if (i < UNIX_BIND_MAX_ITER) {
1596 // try another hash value
1597 hash_value++;
1598 } else {
1599 close(server_fd);
1600 TTCN_Communication::send_connect_error(port_name,
1601 remote_component, remote_port, "Could not find a free "
1602 "pathname to bind the UNIX server socket to after %u "
1603 "iterations.", i);
1604 errno = 0;
1605 return;
1606 }
1607 } else {
1608 close(server_fd);
1609 TTCN_Communication::send_connect_error(port_name, remote_component,
1610 remote_port, "Binding of UNIX server socket to pathname %s "
1611 "failed. (%s)", local_addr.sun_path, strerror(errno));
1612 errno = 0;
1613 return;
1614 }
1615 }
1616
1617 // zero backlog is enough since we are waiting for only one client
1618 if (listen(server_fd, 0)) {
1619 close(server_fd);
1620 TTCN_Communication::send_connect_error(port_name, remote_component,
1621 remote_port, "Listening on UNIX pathname %s failed. (%s)",
1622 local_addr.sun_path, strerror(errno));
1623 errno = 0;
1624 return;
1625 }
1626
1627 if (!TTCN_Communication::set_close_on_exec(server_fd)) {
1628 close(server_fd);
1629 TTCN_Communication::send_connect_error(port_name, remote_component,
1630 remote_port, "Setting the close-on-exec flag failed on the UNIX "
1631 "server socket.");
1632 return;
1633 }
1634
1635 port_connection *new_connection = add_connection(remote_component,
1636 remote_port, TRANSPORT_UNIX_STREAM);
1637 new_connection->connection_state = CONN_LISTENING;
1638 new_connection->stream.comm_fd = server_fd;
1639 Fd_And_Timeout_User::add_fd(server_fd, new_connection, FD_EVENT_RD);
1640
1641 TTCN_Communication::send_connect_listen_ack_unix_stream(port_name,
1642 remote_component, remote_port, &local_addr);
1643
1644 TTCN_Logger::log_port_misc(
1645 TitanLoggerApi::Port__Misc_reason::port__is__waiting__for__connection__unix,
1646 port_name, remote_component, remote_port, local_addr.sun_path);
1647 }
1648
1649 void PORT::connect_local(component remote_component, const char *remote_port)
1650 {
1651 if (self != remote_component) {
1652 TTCN_Communication::send_connect_error(port_name, remote_component,
1653 remote_port, "Message CONNECT with transport type LOCAL refers "
1654 "to a port of another component (%d).", remote_component);
1655 return;
1656 }
1657 PORT *remote_ptr = lookup_by_name(remote_port);
1658 if (remote_ptr == NULL) {
1659 TTCN_Communication::send_connect_error(port_name, remote_component,
1660 remote_port, "Port %s does not exist.", remote_port);
1661 return;
1662 } else if (!remote_ptr->is_active) TTCN_error("Internal error: Port %s is "
1663 "inactive when trying to connect it to local port %s.", remote_port,
1664 port_name);
1665 add_local_connection(remote_ptr);
1666 if (this != remote_ptr) remote_ptr->add_local_connection(this);
1667 TTCN_Communication::send_connected(port_name, remote_component,
1668 remote_port);
1669 }
1670
1671 void PORT::connect_stream(component remote_component, const char *remote_port,
1672 transport_type_enum transport_type, Text_Buf& text_buf)
1673 {
1674 #ifdef WIN32
1675 again:
1676 #endif
1677 const char *transport_str;
1678 int client_fd;
1679 switch (transport_type) {
1680 case TRANSPORT_INET_STREAM:
1681 transport_str = "TCP";
1682 // creating the TCP client socket
1683 client_fd = NetworkHandler::socket(TTCN_Communication::get_network_family());
1684 break;
1685 case TRANSPORT_UNIX_STREAM:
1686 transport_str = "UNIX";
1687 // creating the UNIX client socket
1688 client_fd = socket(PF_UNIX, SOCK_STREAM, 0);
1689 break;
1690 default:
1691 TTCN_error("Internal error: PORT::connect_stream(): invalid transport "
1692 "type (%d).", transport_type);
1693 }
1694 if (client_fd < 0) {
1695 TTCN_Communication::send_connect_error(port_name, remote_component,
1696 remote_port, "Creation of the %s client socket failed. (%s)",
1697 transport_str, strerror(errno));
1698 errno = 0;
1699 return;
1700 }
1701
1702 switch (transport_type) {
1703 case TRANSPORT_INET_STREAM: {
1704 // connect to the IP address and port number given in message CONNECT
1705 IPAddress *remote_addr = IPAddress::create_addr(TTCN_Communication::get_network_family());
1706 remote_addr->pull_raw(text_buf);
1707 #ifdef SOLARIS
1708 if (connect(client_fd, (struct sockaddr*)remote_addr->get_addr(), remote_addr->get_addr_len())) {
1709 #else
1710 if (connect(client_fd, remote_addr->get_addr(), remote_addr->get_addr_len())) {
1711 #endif
1712 #ifdef WIN32
1713 if (errno == EADDRINUSE) {
1714 close(client_fd);
1715 errno = 0;
1716 TTCN_warning("connect() returned error code EADDRINUSE. "
1717 "Perhaps this is a Cygwin bug. Trying to connect again.");
1718 goto again;
1719 }
1720 #endif
1721 close(client_fd);
1722 TTCN_Communication::send_connect_error(port_name, remote_component,
1723 remote_port, "TCP connection establishment failed to %s:%d. (%s)",
1724 remote_addr->get_addr_str(), remote_addr->get_port(), strerror(errno));
1725 errno = 0;
1726 delete remote_addr;
1727 return;
1728 }
1729 delete remote_addr;
1730 break; }
1731 case TRANSPORT_UNIX_STREAM: {
1732 // connect to the UNIX pathname given in the message CONNECT
1733 struct sockaddr_un remote_addr;
1734 memset(&remote_addr, 0, sizeof(remote_addr));
1735 remote_addr.sun_family = AF_UNIX;
1736 size_t pathname_len = text_buf.pull_int().get_val();
1737 if (pathname_len >= sizeof(remote_addr.sun_path)) {
1738 close(client_fd);
1739 TTCN_Communication::send_connect_error(port_name, remote_component,
1740 remote_port, "The UNIX pathname used by the server socket is "
1741 "too long. It consists of %lu bytes although it should be "
1742 "shorter than %lu bytes to fit in the appropriate structure.",
1743 (unsigned long) pathname_len,
1744 (unsigned long) sizeof(remote_addr.sun_path));
1745 return;
1746 }
1747 text_buf.pull_raw(pathname_len, remote_addr.sun_path);
1748 if (connect(client_fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) {
1749 close(client_fd);
1750 TTCN_Communication::send_connect_error(port_name, remote_component,
1751 remote_port, "UNIX socket connection establishment failed to "
1752 "pathname %s. (%s)", remote_addr.sun_path, strerror(errno));
1753 errno = 0;
1754 return;
1755 }
1756 break; }
1757 default:
1758 TTCN_error("Internal error: PORT::connect_stream(): invalid transport "
1759 "type (%d).", transport_type);
1760 }
1761
1762 if (!TTCN_Communication::set_close_on_exec(client_fd)) {
1763 close(client_fd);
1764 TTCN_Communication::send_connect_error(port_name, remote_component,
1765 remote_port, "Setting the close-on-exec flag failed on the %s "
1766 "client socket.", transport_str);
1767 return;
1768 }
1769
1770 if (!TTCN_Communication::set_non_blocking_mode(client_fd, TRUE)) {
1771 close(client_fd);
1772 TTCN_Communication::send_connect_error(port_name, remote_component,
1773 remote_port, "Setting the non-blocking mode failed on the %s "
1774 "client socket.", transport_str);
1775 return;
1776 }
1777
1778 if (transport_type == TRANSPORT_INET_STREAM &&
1779 !TTCN_Communication::set_tcp_nodelay(client_fd)) {
1780 close(client_fd);
1781 TTCN_Communication::send_connect_error(port_name, remote_component,
1782 remote_port, "Setting the TCP_NODELAY flag failed on the TCP "
1783 "client socket.");
1784 return;
1785 }
1786
1787 port_connection *new_connection = add_connection(remote_component,
1788 remote_port, transport_type);
1789 new_connection->connection_state = CONN_CONNECTED;
1790 new_connection->stream.comm_fd = client_fd;
1791 Fd_And_Timeout_User::add_fd(client_fd, new_connection, FD_EVENT_RD);
1792
1793 TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::connection__established,
1794 port_name, remote_component, remote_port, transport_str);
1795 }
1796
1797 void PORT::disconnect_local(port_connection *conn_ptr)
1798 {
1799 PORT *remote_ptr = conn_ptr->local.port_ptr;
1800 remove_local_connection(conn_ptr);
1801 if (this != remote_ptr) {
1802 port_connection *conn2_ptr =
1803 remote_ptr->lookup_connection(self, port_name);
1804 if (conn2_ptr == NULL) TTCN_error("Internal error: Port %s is "
1805 "connected with local port %s, but port %s does not have a "
1806 "connection to %s.", port_name, remote_ptr->port_name,
1807 remote_ptr->port_name, port_name);
1808 else remote_ptr->remove_local_connection(conn2_ptr);
1809 }
1810 TTCN_Communication::send_disconnected(port_name, self,
1811 remote_ptr->port_name);
1812 }
1813
1814 void PORT::disconnect_stream(port_connection *conn_ptr)
1815 {
1816 switch (conn_ptr->connection_state) {
1817 case CONN_LISTENING:
1818 TTCN_Logger::log_port_misc(
1819 TitanLoggerApi::Port__Misc_reason::destroying__unestablished__connection,
1820 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
1821 remove_connection(conn_ptr);
1822 // do not send back any acknowledgment
1823 break;
1824 case CONN_CONNECTED: {
1825 TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::terminating__connection,
1826 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
1827 Text_Buf outgoing_buf;
1828 outgoing_buf.push_int(CONN_DATA_LAST);
1829 if (send_data_stream(conn_ptr, outgoing_buf, TRUE)) {
1830 // sending the last message was successful
1831 // wait for the acknowledgment from the peer
1832 conn_ptr->connection_state = CONN_LAST_MSG_SENT;
1833 } else {
1834 TTCN_Logger::log_port_misc(
1835 TitanLoggerApi::Port__Misc_reason::sending__termination__request__failed,
1836 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
1837 // send an acknowledgment to MC immediately to avoid deadlock
1838 // in case of communication failure
1839 // (i.e. when the peer does not send DISCONNECTED)
1840 TTCN_Communication::send_disconnected(port_name,
1841 conn_ptr->remote_component, conn_ptr->remote_port);
1842 TTCN_warning("The last outgoing messages on port %s may be lost.",
1843 port_name);
1844 // destroy the connection immediately
1845 remove_connection(conn_ptr);
1846 }
1847 break; }
1848 default:
1849 TTCN_error("The connection of port %s to %d:%s is in unexpected "
1850 "state when trying to terminate it.", port_name,
1851 conn_ptr->remote_component, conn_ptr->remote_port);
1852 }
1853 }
1854
1855 void PORT::send_data_local(port_connection *conn_ptr, Text_Buf& outgoing_data)
1856 {
1857 outgoing_data.rewind();
1858 PORT *dest_ptr = conn_ptr->local.port_ptr;
1859 if (this != dest_ptr) {
1860 port_connection *conn2_ptr =
1861 dest_ptr->lookup_connection(self, port_name);
1862 if (conn2_ptr == NULL) TTCN_error("Internal error: Port %s is "
1863 "connected with local port %s, but port %s does not have a "
1864 "connection to %s.", port_name, dest_ptr->port_name,
1865 dest_ptr->port_name, port_name);
1866 dest_ptr->process_data(conn2_ptr, outgoing_data);
1867 } else process_data(conn_ptr, outgoing_data);
1868 }
1869
1870 boolean PORT::send_data_stream(port_connection *conn_ptr,
1871 Text_Buf& outgoing_data, boolean ignore_peer_disconnect)
1872 {
1873 bool would_block_warning=false;
1874 outgoing_data.calculate_length();
1875 const char *msg_ptr = outgoing_data.get_data();
1876 size_t msg_len = outgoing_data.get_len(), sent_len = 0;
1877 while (sent_len < msg_len) {
1878 int ret_val = send(conn_ptr->stream.comm_fd, msg_ptr + sent_len,
1879 msg_len - sent_len, 0);
1880 if (ret_val > 0) sent_len += ret_val;
1881 else {
1882 switch (errno) {
1883 case EINTR:
1884 // a signal occurred: do nothing, just try again
1885 errno = 0;
1886 break;
1887 case EAGAIN: {
1888 // the output buffer is full: try to increase it if possible
1889 errno = 0;
1890 int old_bufsize, new_bufsize;
1891 if (TTCN_Communication::increase_send_buffer(
1892 conn_ptr->stream.comm_fd, old_bufsize, new_bufsize)) {
1893 TTCN_Logger::log_port_misc(
1894 TitanLoggerApi::Port__Misc_reason::sending__would__block,
1895 port_name, conn_ptr->remote_component, conn_ptr->remote_port,
1896 NULL, old_bufsize, new_bufsize);
1897 } else {
1898 if(!would_block_warning){
1899 TTCN_warning_begin("Sending data on the connection of "
1900 "port %s to ", port_name);
1901 COMPONENT::log_component_reference(
1902 conn_ptr->remote_component);
1903 TTCN_Logger::log_event(":%s would block execution and it "
1904 "is not possible to further increase the size of the "
1905 "outgoing buffer. Trying to process incoming data to "
1906 "avoid deadlock.", conn_ptr->remote_port);
1907 TTCN_warning_end();
1908 would_block_warning=true;
1909 }
1910 TTCN_Snapshot::block_for_sending(conn_ptr->stream.comm_fd);
1911 }
1912 break; }
1913 case ECONNRESET:
1914 case EPIPE:
1915 if (ignore_peer_disconnect) return FALSE;
1916 // no break
1917 default:
1918 TTCN_error("Sending data on the connection of port %s to "
1919 "%d:%s failed.", port_name, conn_ptr->remote_component,
1920 conn_ptr->remote_port);
1921 }
1922 }
1923 }
1924 if(would_block_warning){
1925 TTCN_warning_begin("The message finally was sent on "
1926 "port %s to ", port_name);
1927 COMPONENT::log_component_reference(
1928 conn_ptr->remote_component);
1929 TTCN_Logger::log_event(":%s.", conn_ptr->remote_port);
1930 TTCN_warning_end();
1931 }
1932 return TRUE;
1933 }
1934
1935 void PORT::handle_incoming_connection(port_connection *conn_ptr)
1936 {
1937 const char *transport_str =
1938 conn_ptr->transport_type == TRANSPORT_INET_STREAM ? "TCP" : "UNIX";
1939 int comm_fd = accept(conn_ptr->stream.comm_fd, NULL, NULL);
1940 if (comm_fd < 0) {
1941 TTCN_Communication::send_connect_error(port_name,
1942 conn_ptr->remote_component, conn_ptr->remote_port,
1943 "Accepting of incoming %s connection failed. (%s)", transport_str,
1944 strerror(errno));
1945 errno = 0;
1946 remove_connection(conn_ptr);
1947 return;
1948 }
1949
1950 if (!TTCN_Communication::set_close_on_exec(comm_fd)) {
1951 close(comm_fd);
1952 TTCN_Communication::send_connect_error(port_name,
1953 conn_ptr->remote_component, conn_ptr->remote_port,
1954 "Setting the close-on-exec flag failed on the server-side %s "
1955 "socket.", transport_str);
1956 remove_connection(conn_ptr);
1957 return;
1958 }
1959
1960 if (!TTCN_Communication::set_non_blocking_mode(comm_fd, TRUE)) {
1961 close(comm_fd);
1962 TTCN_Communication::send_connect_error(port_name,
1963 conn_ptr->remote_component, conn_ptr->remote_port,
1964 "Setting the non-blocking mode failed on the server-side %s "
1965 "socket.", transport_str);
1966 remove_connection(conn_ptr);
1967 return;
1968 }
1969
1970 if (conn_ptr->transport_type == TRANSPORT_INET_STREAM &&
1971 !TTCN_Communication::set_tcp_nodelay(comm_fd)) {
1972 close(comm_fd);
1973 TTCN_Communication::send_connect_error(port_name,
1974 conn_ptr->remote_component, conn_ptr->remote_port,
1975 "Setting the TCP_NODELAY flag failed on the server-side TCP "
1976 "socket.");
1977 remove_connection(conn_ptr);
1978 return;
1979 }
1980
1981 // shutting down the server socket and replacing it with the
1982 // communication socket of the new connection
1983 Fd_And_Timeout_User::remove_fd(conn_ptr->stream.comm_fd, conn_ptr,
1984 FD_EVENT_RD);
1985 if (conn_ptr->transport_type == TRANSPORT_UNIX_STREAM)
1986 unlink_unix_pathname(conn_ptr->stream.comm_fd);
1987 close(conn_ptr->stream.comm_fd);
1988 conn_ptr->connection_state = CONN_CONNECTED;
1989 conn_ptr->stream.comm_fd = comm_fd;
1990 Fd_And_Timeout_User::add_fd(comm_fd, conn_ptr, FD_EVENT_RD);
1991
1992 TTCN_Communication::send_connected(port_name, conn_ptr->remote_component,
1993 conn_ptr->remote_port);
1994
1995 TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::connection__accepted,
1996 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
1997 }
1998
1999 void PORT::handle_incoming_data(port_connection *conn_ptr)
2000 {
2001 if (conn_ptr->stream.incoming_buf == NULL)
2002 conn_ptr->stream.incoming_buf = new Text_Buf;
2003 Text_Buf& incoming_buf = *conn_ptr->stream.incoming_buf;
2004 char *buf_ptr;
2005 int buf_len;
2006 incoming_buf.get_end(buf_ptr, buf_len);
2007 int recv_len = recv(conn_ptr->stream.comm_fd, buf_ptr, buf_len, 0);
2008 if (recv_len < 0) {
2009 // an error occurred
2010 if (errno == ECONNRESET) {
2011 // TCP connection was reset by peer
2012 errno = 0;
2013 TTCN_Communication::send_disconnected(port_name,
2014 conn_ptr->remote_component, conn_ptr->remote_port);
2015 TTCN_Logger::log_port_misc(
2016 TitanLoggerApi::Port__Misc_reason::connection__reset__by__peer,
2017 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
2018 TTCN_warning("The last outgoing messages on port %s may be lost.",
2019 port_name);
2020 conn_ptr->connection_state = CONN_IDLE;
2021 } else {
2022 TTCN_error("Receiving data on the connection of port %s from "
2023 "%d:%s failed.", port_name, conn_ptr->remote_component,
2024 conn_ptr->remote_port);
2025 }
2026 } else if (recv_len > 0) {
2027 // data was received
2028 incoming_buf.increase_length(recv_len);
2029 // processing all messages in the buffer after each other
2030 while (incoming_buf.is_message()) {
2031 incoming_buf.pull_int(); // message_length
2032 process_data(conn_ptr, incoming_buf);
2033 incoming_buf.cut_message();
2034 }
2035 } else {
2036 // the connection was closed by the peer
2037 TTCN_Communication::send_disconnected(port_name,
2038 conn_ptr->remote_component, conn_ptr->remote_port);
2039 if (conn_ptr->connection_state != CONN_LAST_MSG_RCVD) {
2040 TTCN_Logger::log_port_misc(
2041 TitanLoggerApi::Port__Misc_reason::connection__closed__by__peer,
2042 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
2043 }
2044 // the connection can be removed
2045 conn_ptr->connection_state = CONN_IDLE;
2046 }
2047 if (conn_ptr->connection_state == CONN_IDLE) {
2048 // terminating and removing connection
2049 int msg_len = incoming_buf.get_len();
2050 if (msg_len > 0) {
2051 TTCN_warning_begin("Message fragment remained in the buffer of "
2052 "port connection between %s and ", port_name);
2053 COMPONENT::log_component_reference(conn_ptr->remote_component);
2054 TTCN_Logger::log_event(":%s: ", conn_ptr->remote_port);
2055 const unsigned char *msg_ptr =
2056 (const unsigned char*)incoming_buf.get_data();
2057 for (int i = 0; i < msg_len; i++)
2058 TTCN_Logger::log_octet(msg_ptr[i]);
2059 TTCN_warning_end();
2060 }
2061
2062 TTCN_Logger::log_port_misc(TitanLoggerApi::Port__Misc_reason::port__disconnected,
2063 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
2064 remove_connection(conn_ptr);
2065 }
2066 }
2067
2068 void PORT::process_last_message(port_connection *conn_ptr)
2069 {
2070 switch (conn_ptr->transport_type) {
2071 case TRANSPORT_INET_STREAM:
2072 case TRANSPORT_UNIX_STREAM:
2073 break;
2074 default:
2075 TTCN_error("Internal error: Connection termination request was "
2076 "received on the connection of port %s with %d:%s, which has an "
2077 "invalid transport type (%d).", port_name,
2078 conn_ptr->remote_component, conn_ptr->remote_port,
2079 conn_ptr->transport_type);
2080 }
2081 switch (conn_ptr->connection_state) {
2082 case CONN_CONNECTED: {
2083 TTCN_Logger::log_port_misc(
2084 TitanLoggerApi::Port__Misc_reason::termination__request__received,
2085 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
2086 Text_Buf outgoing_buf;
2087 outgoing_buf.push_int(CONN_DATA_LAST);
2088 if (send_data_stream(conn_ptr, outgoing_buf, TRUE)) {
2089 // sending the last message was successful
2090 // wait until the peer closes the transport connection
2091 conn_ptr->connection_state = CONN_LAST_MSG_RCVD;
2092 } else {
2093 TTCN_Logger::log_port_misc(
2094 TitanLoggerApi::Port__Misc_reason::acknowledging__termination__request__failed,
2095 port_name, conn_ptr->remote_component, conn_ptr->remote_port);
2096 // send an acknowledgment to MC immediately to avoid deadlock
2097 // in case of communication failure
2098 // (i.e. when the peer does not send DISCONNECTED)
2099 TTCN_Communication::send_disconnected(port_name,
2100 conn_ptr->remote_component, conn_ptr->remote_port);
2101 // the connection can be removed immediately
2102 TTCN_warning("The last outgoing messages on port %s may be lost.",
2103 port_name);
2104 conn_ptr->connection_state = CONN_IDLE;
2105 }
2106 break; }
2107 case CONN_LAST_MSG_SENT:
2108 // the connection can be removed
2109 conn_ptr->connection_state = CONN_IDLE;
2110 break;
2111 case CONN_LAST_MSG_RCVD:
2112 case CONN_IDLE:
2113 TTCN_warning("Unexpected data arrived after the indication of "
2114 "connection termination on port %s from %d:%s.", port_name,
2115 conn_ptr->remote_component, conn_ptr->remote_port);
2116 break;
2117 default:
2118 TTCN_error("Internal error: Connection of port %s with %d:%s has "
2119 "invalid state (%d).", port_name, conn_ptr->remote_component,
2120 conn_ptr->remote_port, conn_ptr->connection_state);
2121 }
2122 }
2123
2124 void PORT::map(const char *system_port)
2125 {
2126 if (!is_active) TTCN_error("Inactive port %s cannot be mapped.", port_name);
2127
2128 int new_posn;
2129 for (new_posn = 0; new_posn < n_system_mappings; new_posn++) {
2130 int str_diff = strcmp(system_port, system_mappings[new_posn]);
2131 if (str_diff < 0) break;
2132 else if (str_diff == 0) {
2133 TTCN_warning("Port %s is already mapped to system:%s."
2134 " Map operation was ignored.", port_name, system_port);
2135 return;
2136 }
2137 }
2138
2139 set_system_parameters(system_port);
2140
2141 user_map(system_port);
2142
2143 TTCN_Logger::log_port_misc(
2144 TitanLoggerApi::Port__Misc_reason::port__was__mapped__to__system,
2145 port_name, SYSTEM_COMPREF, system_port);
2146
2147 // the mapping shall be registered in the table only if user_map() was
2148 // successful
2149 system_mappings = (char**)Realloc(system_mappings,
2150 (n_system_mappings + 1) * sizeof(*system_mappings));
2151 memmove(system_mappings + new_posn + 1, system_mappings + new_posn,
2152 (n_system_mappings - new_posn) * sizeof(*system_mappings));
2153 system_mappings[new_posn] = mcopystr(system_port);
2154 n_system_mappings++;
2155
2156 if (n_system_mappings > 1) TTCN_warning("Port %s has now more than one "
2157 "mappings. Message cannot be sent on it to system even with explicit "
2158 "addressing.", port_name);
2159 }
2160
2161 void PORT::unmap(const char *system_port)
2162 {
2163 int del_posn;
2164 for (del_posn = 0; del_posn < n_system_mappings; del_posn++) {
2165 int str_diff = strcmp(system_port, system_mappings[del_posn]);
2166 if (str_diff == 0) break;
2167 else if (str_diff < 0) {
2168 del_posn = n_system_mappings;
2169 break;
2170 }
2171 }
2172 if (del_posn >= n_system_mappings) {
2173 TTCN_warning("Port %s is not mapped to system:%s. "
2174 "Unmap operation was ignored.", port_name, system_port);
2175 return;
2176 }
2177
2178 char *unmapped_port = system_mappings[del_posn];
2179
2180 // first remove the mapping from the table
2181 n_system_mappings--;
2182 memmove(system_mappings + del_posn, system_mappings + del_posn + 1,
2183 (n_system_mappings - del_posn) * sizeof(*system_mappings));
2184 system_mappings = (char**)Realloc(system_mappings,
2185 n_system_mappings * sizeof(*system_mappings));
2186
2187 try {
2188 user_unmap(system_port);
2189 } catch (...) {
2190 // prevent from memory leak
2191 Free(unmapped_port);
2192 throw;
2193 }
2194
2195 TTCN_Logger::log_port_misc(
2196 TitanLoggerApi::Port__Misc_reason::port__was__unmapped__from__system,
2197 port_name, SYSTEM_COMPREF, system_port);
2198
2199 Free(unmapped_port);
2200 }
2201
2202 void PORT::process_connect_listen(const char *local_port,
2203 component remote_component, const char *remote_port,
2204 transport_type_enum transport_type)
2205 {
2206 PORT *port_ptr = lookup_by_name(local_port);
2207 if (port_ptr == NULL) {
2208 TTCN_Communication::send_connect_error(local_port, remote_component,
2209 remote_port, "Port %s does not exist.", local_port);
2210 return;
2211 } else if (!port_ptr->is_active) {
2212 TTCN_error("Internal error: Port %s is inactive when trying to "
2213 "connect it to %d:%s.", local_port, remote_component, remote_port);
2214 } else if (port_ptr->lookup_connection(remote_component, remote_port)
2215 != NULL) {
2216 TTCN_Communication::send_connect_error(local_port, remote_component,
2217 remote_port, "Port %s already has a connection towards %d:%s.",
2218 local_port, remote_component, remote_port);
2219 return;
2220 } else if (port_ptr->lookup_connection_to_compref(remote_component, NULL)
2221 != NULL) {
2222 TTCN_warning_begin("Port %s will have more than one connections with "
2223 "ports of test component ", local_port);
2224 COMPONENT::log_component_reference(remote_component);
2225 TTCN_Logger::log_event_str(". These connections cannot be used for "
2226 "sending even with explicit addressing.");
2227 TTCN_warning_end();
2228 }
2229
2230 switch (transport_type) {
2231 case TRANSPORT_LOCAL:
2232 TTCN_Communication::send_connect_error(local_port, remote_component,
2233 remote_port, "Message CONNECT_LISTEN cannot refer to transport "
2234 "type LOCAL.");
2235 break;
2236 case TRANSPORT_INET_STREAM:
2237 port_ptr->connect_listen_inet_stream(remote_component, remote_port);
2238 break;
2239 case TRANSPORT_UNIX_STREAM:
2240 port_ptr->connect_listen_unix_stream(remote_component, remote_port);
2241 break;
2242 default:
2243 TTCN_Communication::send_connect_error(local_port, remote_component,
2244 remote_port, "Message CONNECT_LISTEN refers to invalid transport "
2245 "type (%d).", transport_type);
2246 break;
2247 }
2248 }
2249
2250 void PORT::process_connect(const char *local_port,
2251 component remote_component, const char *remote_port,
2252 transport_type_enum transport_type, Text_Buf& text_buf)
2253 {
2254 PORT *port_ptr = lookup_by_name(local_port);
2255 if (port_ptr == NULL) {
2256 TTCN_Communication::send_connect_error(local_port, remote_component,
2257 remote_port, "Port %s does not exist.", local_port);
2258 return;
2259 } else if (!port_ptr->is_active) {
2260 TTCN_error("Internal error: Port %s is inactive when trying to "
2261 "connect it to %d:%s.", local_port, remote_component, remote_port);
2262 } else if (port_ptr->lookup_connection(remote_component, remote_port)
2263 != NULL) {
2264 TTCN_Communication::send_connect_error(local_port, remote_component,
2265 remote_port, "Port %s already has a connection towards %d:%s.",
2266 local_port, remote_component, remote_port);
2267 return;
2268 } else if (port_ptr->lookup_connection_to_compref(remote_component, NULL)
2269 != NULL) {
2270 TTCN_warning_begin("Port %s will have more than one connections with "
2271 "ports of test component ", local_port);
2272 COMPONENT::log_component_reference(remote_component);
2273 TTCN_Logger::log_event_str(". These connections cannot be used for "
2274 "sending even with explicit addressing.");
2275 TTCN_warning_end();
2276 }
2277
2278 switch (transport_type) {
2279 case TRANSPORT_LOCAL:
2280 port_ptr->connect_local(remote_component, remote_port);
2281 break;
2282 case TRANSPORT_INET_STREAM:
2283 case TRANSPORT_UNIX_STREAM:
2284 port_ptr->connect_stream(remote_component, remote_port, transport_type,
2285 text_buf);
2286 break;
2287 default:
2288 TTCN_Communication::send_connect_error(local_port, remote_component,
2289 remote_port, "Message CONNECT refers to invalid transport type "
2290 "(%d).", transport_type);
2291 break;
2292 }
2293 }
2294
2295 void PORT::process_disconnect(const char *local_port,
2296 component remote_component, const char *remote_port)
2297 {
2298 PORT *port_ptr = lookup_by_name(local_port);
2299 if (port_ptr == NULL) {
2300 TTCN_Communication::send_error("Message DISCONNECT refers to "
2301 "non-existent local port %s.", local_port);
2302 return;
2303 } else if (!port_ptr->is_active) {
2304 TTCN_error("Internal error: Port %s is inactive when trying to "
2305 "disconnect it from %d:%s.", local_port, remote_component,
2306 remote_port);
2307 }
2308 port_connection *conn_ptr = port_ptr->lookup_connection(remote_component,
2309 remote_port);
2310 if (conn_ptr == NULL) {
2311 // the connection does not exist
2312 if (self == remote_component && lookup_by_name(remote_port) == NULL) {
2313 // the remote endpoint is in the same component,
2314 // but it does not exist
2315 TTCN_Communication::send_error("Message DISCONNECT refers to "
2316 "non-existent port %s.", remote_port);
2317 } else {
2318 TTCN_Communication::send_disconnected(local_port, remote_component,
2319 remote_port);
2320 }
2321 return;
2322 }
2323 switch (conn_ptr->transport_type) {
2324 case TRANSPORT_LOCAL:
2325 port_ptr->disconnect_local(conn_ptr);
2326 break;
2327 case TRANSPORT_INET_STREAM:
2328 case TRANSPORT_UNIX_STREAM:
2329 port_ptr->disconnect_stream(conn_ptr);
2330 break;
2331 default:
2332 TTCN_error("Internal error: The connection of port %s to %d:%s has "
2333 "invalid transport type (%d) when trying to terminate the "
2334 "connection.", local_port, remote_component, remote_port,
2335 conn_ptr->transport_type);
2336 }
2337 }
2338
2339 void PORT::make_local_connection(const char *src_port, const char *dest_port)
2340 {
2341 PORT *src_ptr = lookup_by_name(src_port);
2342 if (src_ptr == NULL) TTCN_error("Connect operation refers to "
2343 "non-existent port %s.", src_port);
2344 else if (!src_ptr->is_active) TTCN_error("Internal error: Port %s is "
2345 "inactive when trying to connect it with local port %s.", src_port,
2346 dest_port);
2347 else if (src_ptr->lookup_connection(MTC_COMPREF, dest_port) != NULL) {
2348 TTCN_warning("Port %s is already connected with local port %s. "
2349 "Connect operation had no effect.", src_port, dest_port);
2350 return;
2351 } else if (src_ptr->lookup_connection_to_compref(MTC_COMPREF, NULL)
2352 != NULL) {
2353 TTCN_warning("Port %s will have more than one connections with local "
2354 "ports. These connections cannot be used for communication even "
2355 "with explicit addressing.", src_port);
2356 }
2357 PORT *dest_ptr = lookup_by_name(dest_port);
2358 if (dest_ptr == NULL) TTCN_error("Connect operation refers to "
2359 "non-existent port %s.", dest_port);
2360 else if (!dest_ptr->is_active) TTCN_error("Internal error: Port %s is "
2361 "inactive when trying to connect it with local port %s.", dest_port,
2362 src_port);
2363 src_ptr->add_local_connection(dest_ptr);
2364 if (src_ptr != dest_ptr) dest_ptr->add_local_connection(src_ptr);
2365 }
2366
2367 void PORT::terminate_local_connection(const char *src_port,
2368 const char *dest_port)
2369 {
2370 PORT *src_ptr = lookup_by_name(src_port);
2371 if (src_ptr == NULL) TTCN_error("Disconnect operation refers to "
2372 "non-existent port %s.", src_port);
2373 else if (!src_ptr->is_active) TTCN_error("Internal error: Port %s is "
2374 "inactive when trying to disconnect it from local port %s.", src_port,
2375 dest_port);
2376 port_connection *conn_ptr = src_ptr->lookup_connection(MTC_COMPREF,
2377 dest_port);
2378 if (conn_ptr != NULL) {
2379 PORT *dest_ptr = conn_ptr->local.port_ptr;
2380 src_ptr->remove_local_connection(conn_ptr);
2381 if (src_ptr != dest_ptr) {
2382 if (!dest_ptr->is_active) TTCN_error("Internal error: Port %s is "
2383 "inactive when trying to disconnect it from local port %s.",
2384 dest_port, src_port);
2385 port_connection *conn2_ptr =
2386 dest_ptr->lookup_connection(MTC_COMPREF, src_port);
2387 if (conn2_ptr == NULL) TTCN_error("Internal error: Port %s is "
2388 "connected with local port %s, but port %s does not have a "
2389 "connection to %s.", src_port, dest_port, dest_port, src_port);
2390 else dest_ptr->remove_local_connection(conn2_ptr);
2391 }
2392 } else {
2393 PORT *dest_ptr = lookup_by_name(dest_port);
2394 if (dest_ptr == NULL) TTCN_error("Disconnect operation refers to "
2395 "non-existent port %s.", dest_port);
2396 else if (src_ptr != dest_ptr) {
2397 if (!dest_ptr->is_active) TTCN_error("Internal error: Port %s is "
2398 "inactive when trying to disconnect it from local port %s.",
2399 dest_port, src_port);
2400 else if (dest_ptr->lookup_connection(MTC_COMPREF, src_port) != NULL)
2401 TTCN_error("Internal error: Port %s is connected with local "
2402 "port %s, but port %s does not have a connection to %s.",
2403 dest_port, src_port, src_port, dest_port);
2404 }
2405 TTCN_warning("Port %s does not have connection with local port %s. "
2406 "Disconnect operation had no effect.", src_port, dest_port);
2407 }
2408 }
2409
2410 void PORT::map_port(const char *component_port, const char *system_port)
2411 {
2412 PORT *port_ptr = lookup_by_name(component_port);
2413 if (port_ptr == NULL) TTCN_error("Map operation refers to "
2414 "non-existent port %s.", component_port);
2415 port_ptr->map(system_port);
2416 if (!TTCN_Runtime::is_single())
2417 TTCN_Communication::send_mapped(component_port, system_port);
2418 }
2419
2420 void PORT::unmap_port(const char *component_port, const char *system_port)
2421 {
2422 PORT *port_ptr = lookup_by_name(component_port);
2423 if (port_ptr == NULL) TTCN_error("Unmap operation refers to "
2424 "non-existent port %s.", component_port);
2425 port_ptr->unmap(system_port);
2426 if (!TTCN_Runtime::is_single())
2427 TTCN_Communication::send_unmapped(component_port, system_port);
2428 }
This page took 0.081796 seconds and 5 git commands to generate.