Commit | Line | Data |
---|---|---|
d44e3c4f | 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 | ******************************************************************************/ | |
970ed795 EL |
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 | } |