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