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
13 ******************************************************************************/
14 /*******************************************************************************
16 * Zoltan Janos Szabo (Ericsson) - initial architecture design and implementation
17 * Roland Gecse (Ericsson) - initial architecture design
18 * Akos Cserveni (Ericsson) - Basic AST in compiler, semantic checking
19 * Gabor Szalai (Ericsson) - RAW and TEXT codecs
20 * Matyas Forstner (Ericsson) - ASN.1 extension of the compiler and BER/CER/DER codecs
21 * Kristof Szabados (Ericsson) - Eclipse Designer, Executor, Titanium UIs
22 * Szabolcs Beres (Ericsson) - Eclipse LogViewer
23 * Ferenc Kovacs (Ericsson) - log interfaces, big number support, subtype checking
24 * Csaba Raduly (Ericsson) - ASN.1 additions, XML encoder/decoder
25 * Adam Delic (Ericsson) - template restrictions, try&catch, support of pre-processor directives in Eclipse
26 * Krisztian Pandi (Ericsson) - import of imports
27 * Peter Dimitrov (Ericsson)- maintenance
28 * Balazs Andor Zalanyi (Ericsson) - code splitting
29 * Gabor Szalai (Ericsson) - RAW encoding/decoding
30 * Jeno Attila Balasko (Ericsson) - tests, maintenance
31 * Csaba Feher (Ericsson) - epoll support
32 * Tamas Buti (Ericsson)- maintenance
33 * Matyas Ormandi (Ericsson) - maintenance
34 * Botond Baranyi (Ericsson) - JSON encoder
35 * Arpad Lovassy (Ericsson) - Java Executor API
36 * Laszlo Baji (Ericsson) - maintenance
37 * Marton Godar (Ericsson) - xsd2ttcn converter
38 *******************************************************************************/
40 // File: PIPEasp_PT.cc
41 // Description: Source code of PIPE testport implementation
43 // Prodnr: CNL 113 334
44 // Updated: 2008-06-03
45 // Contact: http://ttcn.ericsson.se
49 #include "PIPEasp_PT.hh"
50 #include <signal.h> //kill
51 #include <unistd.h> //pipe
52 #include <errno.h> //errno
53 #include <ctype.h> //isspace
54 #include <sys/select.h> //FD_ZERO
55 #include <stdio.h> // sys_errlist
56 #include <sys/types.h> //wait
57 #include <sys/wait.h> //wait
60 #define PIPE_BUF_SIZE 655536
63 namespace PIPEasp__PortType
{
65 PIPEasp__PT::PIPEasp__PT(const char *par_port_name
)
66 : PIPEasp__PT_BASE(par_port_name
)
68 , processExecuting(false)
71 , processPid(-1) // pid of the process currently executing
72 , processStdin(-1) // fd of stdin of the process
73 , processStdout(-1) // fd of stdout of the process
74 , processStderr(-1) // fd of stderr of the process
75 , processExitCode(0) // exit code of the process
78 stdout_buffer
.clear();
79 stderr_buffer
.clear();
82 PIPEasp__PT::~PIPEasp__PT()
87 void PIPEasp__PT::set_parameter(const char *parameter_name
,
88 const char *parameter_value
)
90 // no config parameters
93 void PIPEasp__PT::Event_Handler(const fd_set
*read_fds
,
94 const fd_set
*write_fds
, const fd_set
*error_fds
,
95 double time_since_last_call
)
97 log("PIPEasp__PT::Event_Handler called");
98 if (processStdout
!= -1 && FD_ISSET(processStdout
, read_fds
)) {
99 if (!processExecuting
) {
100 TTCN_warning("Unexpected message from stdout, no command is executing");
102 log("Incoming stdout message received from process");
108 nBytes
= PIPE_BUF_SIZE
;
109 unsigned char* buffer
;
110 size_t end_len
= nBytes
;
111 stdout_buffer
.get_end(buffer
, end_len
);
112 r
= read(processStdout
,buffer
,(int)nBytes
);
114 log("ttcn_pipe_port: read problem on stdout.\n");
115 // close the pipe and remove it from event handler
116 close(processStdout
);
117 FD_CLR(processStdout
, &readfds
);
118 Install_Handler(&readfds
, NULL
, NULL
, 0.0);
121 // check if stderr is closed:
122 if (processStderr
== -1) {
124 log("Child might have died.");
129 log("incoming stdout %ld bytes\n", r
);
130 stdout_buffer
.increase_length(r
);
135 if (processStderr
!= -1 && FD_ISSET(processStderr
, read_fds
)) {
136 if (!processExecuting
) {
137 TTCN_warning("Unexpected message from stderr, no command is executing");
139 log("Incoming stderr message received from process");
145 nBytes
= PIPE_BUF_SIZE
;
146 unsigned char* buffer
;
147 size_t end_len
= nBytes
;
148 stderr_buffer
.get_end(buffer
, end_len
);
149 r
= read(processStderr
,buffer
,(int)nBytes
);
151 log("ttcn_pipe_port: read problem on stderr.\n");
152 // close the pipe and remove it from event handler
153 close(processStderr
);
154 FD_CLR(processStderr
, &readfds
);
155 Install_Handler(&readfds
, NULL
, NULL
, 0.0);
158 // check if stdout is closed:
159 if (processStdout
== -1) {
161 log("Child might have died.");
166 log("incoming stderr %ld bytes\n", r
);
167 stderr_buffer
.increase_length(r
);
174 void PIPEasp__PT::user_map(const char *system_port
)
179 void PIPEasp__PT::user_unmap(const char *system_port
)
184 void PIPEasp__PT::user_start()
189 void PIPEasp__PT::user_stop()
194 /*************************************
195 * Specific outgoing_send functions
196 *************************************/
197 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PExecute
& send_par
) {
198 log("PIPEasp__PT::outgoing_send_PExecute called");
199 // disable sendStdout, sendStderr until process exits
200 if (processExecuting
) {
201 sendError("Pipe Test Port: Command already executing");
202 TTCN_Logger::begin_event(TTCN_DEBUG
);
203 TTCN_Logger::log_event("PIPE test port (%s): Command already executing. Following ASP is ignored: ", get_name());
205 TTCN_Logger::end_event();
208 PIPEasp__Types::ASP__PExecuteBackground message_PExecuteBackground
;
210 message_PExecuteBackground
.command() = send_par
.command();
211 outgoing_send(message_PExecuteBackground
);
213 PIPEasp__Types::ASP__PStdin message_PStdin
;
214 message_PStdin
.stdin_() = send_par
.stdin_();
215 outgoing_send(message_PStdin
);
218 // closing stdin pipe:
219 outgoing_send(PIPEasp__Types::ASP__PEndOfInput());
221 log("PIPEasp__PT::outgoing_send_PExecute exited");
224 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PExecuteBinary
& send_par
) {
225 log("PIPEasp__PT::outgoing_send_PExecuteBinary called");
226 // disable sendStdout, sendStderr until process exits
227 if (processExecuting
) {
228 sendError("Pipe Test Port: Command already executing");
229 TTCN_Logger::begin_event(TTCN_DEBUG
);
230 TTCN_Logger::log_event("PIPE test port (%s): Command already executing. Following ASP is ignored: ", get_name());
232 TTCN_Logger::end_event();
235 PIPEasp__Types::ASP__PExecuteBackground message_PExecuteBackground
;
237 message_PExecuteBackground
.command() = send_par
.command();
238 outgoing_send(message_PExecuteBackground
);
240 PIPEasp__Types::ASP__PStdinBinary message_PStdinBinary
;
241 message_PStdinBinary
.stdin_() = send_par
.stdin_();
242 outgoing_send(message_PStdinBinary
);
245 // closing stdin pipe:
246 outgoing_send(PIPEasp__Types::ASP__PEndOfInput());
248 log("PIPEasp__PT::outgoing_send_PExecuteBinary exited");
251 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PExecuteBackground
& send_par
) {
252 log("PIPEasp__PT::outgoing_send_PExecuteBackground called");
254 if (processExecuting
) {
255 log("Process already executing. Cannot start new process.");
256 sendError("Pipe Test Port: Command already executing");
257 TTCN_Logger::begin_event(TTCN_DEBUG
);
258 TTCN_Logger::log_event("PIPE test port (%s): Command already executing. Following ASP is ignored: ", get_name());
260 TTCN_Logger::end_event();
261 log("PIPEasp__PT::outgoing_send_PExecuteBackground exited");
265 // creating pipes for process
270 if (pipe(pipesStdin
) != 0) {
271 sendError("Pipe Test Port: Cannot create stdin pipe");
272 log("PIPEasp__PT::outgoing_send_PExecuteBackground exited");
275 if (pipe(pipesStdout
) != 0) {
276 sendError("Pipe Test Port: Cannot create stdout pipe");
277 log("PIPEasp__PT::outgoing_send_PExecuteBackground exited");
280 if (pipe(pipesStderr
) != 0) {
281 sendError("Pipe Test Port: Cannot create stderr pipe");
282 log("PIPEasp__PT::outgoing_send_PExecuteBackground exited");
286 processStdin
= pipesStdin
[1];
287 processStdout
= pipesStdout
[0];
288 processStderr
= pipesStderr
[0];
291 if (processPid
< 0) {
297 close(pipesStdin
[0]);
298 close(pipesStdout
[1]);
299 close(pipesStderr
[1]);
302 close(processStdout
);
303 close(processStderr
);
305 sendError("Pipe Test Port: Cannot fork");
307 else if (processPid
== 0) {
313 // close the parent end of the pipes
315 close(processStdout
);
316 close(processStderr
);
318 /*// set these to the other end of the pipe
319 processStdin = pipesStdin[0];
320 processStdout = pipesStdout[1];
321 processStderr = pipesStderr[1];*/
324 // redirect pipeStdin to stdin
325 r
= dup2(pipesStdin
[0], 0);
327 TTCN_error("Cannot redirect stdin");
331 // redirect pipeStdout to stdout
332 r
= dup2(pipesStdout
[1], 1);
334 TTCN_error("Cannot redirect stdout");
338 // redirect pipeStderr to stderr
339 r
= dup2(pipesStderr
[1], 2);
341 TTCN_error("Cannot redirect stderr");
345 processExitCode
= execCommand((const char*)send_par
.command());
347 // There is a problem executing the command
354 close(pipesStdin
[0]);
355 close(pipesStdout
[1]);
356 close(pipesStderr
[1]);
358 exit(processExitCode
); // end of child process
366 log("Process started with pid: %d", processPid
);
367 // close child end of the pipes
368 close(pipesStdin
[0]);
369 close(pipesStdout
[1]);
370 close(pipesStderr
[1]);
373 processExecuting
= true;
375 // install handler for the process pipes:
376 //FD_SET(processStdin, &readfds);
377 FD_SET(processStdout
, &readfds
);
378 FD_SET(processStderr
, &readfds
);
380 Install_Handler(&readfds
, NULL
, NULL
, 0.0);
383 log("PIPEasp__PT::outgoing_send_PExecuteBackground exited");
386 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PStdin
& send_par
) {
388 log("PIPEasp__PT::outgoing_send_PStdin called");
390 if (!processExecuting
) {
391 sendError("Pipe Test Port: No command executing");
394 if (disableSend
) { // process was started with PExecute(Binary)
395 sendError("Pipe Test Port: PStdin is not sent: current process is not started with PExecuteBackground!");
399 log("will now write to stdin: '%s'",
400 (const char*)(send_par
.stdin_()+((lineMode
)?"\n":"")));
402 (const char*)(send_par
.stdin_()+((lineMode
)?"\n":"")),
403 send_par
.stdin_().lengthof()+((lineMode
)?1:0));
405 log("PIPEasp__PT::outgoing_send_PStdin exited");
408 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PStdinBinary
& send_par
) {
409 log("PIPEasp__PT::outgoing_send_PStdinBinary called");
411 if (!processExecuting
) {
412 sendError("Pipe Test Port: No command executing");
415 if (disableSend
) { // process was started with PExecute(Binary)
416 sendError("Pipe Test Port: PStdinBinary is not sent: current process is not started with PExecuteBackground!");
420 TTCN_Logger::begin_event(TTCN_DEBUG
);
421 TTCN_Logger::log_event("PIPE test port (%s): will now write binary data to stdin: ", get_name());
422 send_par
.stdin_().log();
423 TTCN_Logger::end_event();
426 (const char*)(const unsigned char*)(send_par
.stdin_()),
427 send_par
.stdin_().lengthof());
428 log("PIPEasp__PT::outgoing_send_PStdinBinary exited");
431 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PKill
& send_par
) {
432 log("PIPEasp__PT::outgoing_send_PKill called");
433 if (!processExecuting
) {
434 // no process is running
435 log("No process executing.");
436 sendError("Pipe Test Port: No command executing");
437 log("PIPEasp__PT::outgoing_send_PKill exited");
441 int signo
= (int)send_par
.signal();
442 if (signo
<1 || signo
>31) {
443 // signo out of range;
444 log("Signo out of range.");
446 "Pipe Test port: Signal number should be "
448 log("PIPEasp__PT::outgoing_send_PKill exited");
452 log("Killing process %d, signo: %d", processPid
, signo
);
453 int r
= kill(processPid
, signo
);
454 log("Kill process returned %d", r
);
455 log("PIPEasp__PT::outgoing_send_PKill exited");
458 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PLineMode
& send_par
) {
459 log("PIPEasp__PT::outgoing_send_PLineMode called");
460 lineMode
= (bool)send_par
.lineMode();
461 log("LineMode is set to %s", (lineMode
)?"TRUE":"FALSE");
462 log("PIPEasp__PT::outgoing_send_PLineMode exited");
465 void PIPEasp__PT::outgoing_send(const PIPEasp__Types::ASP__PEndOfInput
& send_par
) {
466 log("PIPEasp__PT::outgoing_send_PEndOfInput called");
471 log("PIPEasp__PT::outgoing_send_PEndOfInput exited");
474 /********************************
475 * Execute the given command
476 * returns the exitcode of the process
477 *********************************/
478 int PIPEasp__PT::execCommand(const char* command
) {
479 log("PIPEasp__PT::execCommand called");
480 log("Executing command: %s", command
);
482 // with this it is not possible to access the pid of the process
483 //return system(command);
488 CHARSTRING temp
= "";
489 for (int i
= 0; command
[i
] != 0; i
++) {
490 if (isspace(command
[i
])) {
491 argv
[argc
++] = strdup(temp
);
492 log("command argument added: %s", (const char*)temp
);
493 while (command
[i
] != '0' && isspace(command
[i
])) i
++;
497 temp
= temp
+ CHARSTRING(1, command
+i
);
502 argv
[argc
++] = strdup(temp
);
503 log("command argument added: %s", (const char*)temp
);
506 argv
[argc
++] = (char*)NULL
;
508 log("execCommand(%s,%d)\n", argv
[0], argc
);
509 execvp(argv
[0],argv
);
511 fprintf(stderr
,"Error executing command %s (%d): %s\n",
512 argv
[0], errno
, strerror(errno
));
518 /***********************************
519 * if the the child process died, gets
520 * the exit code and sends it to TTCN
521 * should be called when stdout/err are closed
522 ************************************/
523 void PIPEasp__PT::handle_childDeath() {
524 log("Getting process status for pid %d...", processPid
);
525 processExitCode
= 0; // reset the exitcode
526 int pid
= wait(&processExitCode
);
527 //waitpid(processPid,&processExitCode, 0);
528 if (pid
!=processPid
) {
529 log("other child died with pid: %d, exit code: %d", pid
, processExitCode
);
533 log("Child process exit status is: %d", processExitCode
);
534 // send code to TTCN:
536 // send result to TTCN
539 // removing fd-s installed for the process:
540 Uninstall_Handler(); // no handler is needed
544 //FD_CLR(processStdin, &readfds);
545 FD_CLR(processStdout, &readfds);
546 FD_CLR(processStderr, &readfds);
547 Install_Handler(&readfds, NULL, NULL, 0.0);
552 //close(processStdout); // already closed
553 //close(processStderr); // already closed
556 //processStdout = -1;
557 //processStderr = -1;
559 processExecuting
= false;
563 /***************************
564 * Send stdout msg to TTCN
565 ***************************/
566 void PIPEasp__PT::sendStdout() {
567 if (disableSend
) return;
569 PIPEasp__Types::ASP__PStdout message_PStdout
;
570 PIPEasp__Types::ASP__PStdoutBinary message_PStdoutBinary
;
571 if (lineMode
&& !binaryMode
) {
572 // send complete lines from buffer
573 const unsigned char* pos
= stdout_buffer
.get_read_data();
574 for(unsigned int i
=0; i
<stdout_buffer
.get_read_len(); i
++) {
576 if (pos
[i
] != '\n') {
581 // length of data is i (+1 is for \n and is not sent)
582 message_PStdout
.stdout_() = CHARSTRING(i
, (const char*)pos
);
585 incoming_message(message_PStdout
);
587 // remove the complete line from buffer,
588 // also set i and pos to the beginning of buffer
589 stdout_buffer
.set_pos(i
+1);
592 pos
= stdout_buffer
.get_read_data();
595 // lineMode false or binaryMode true
597 message_PStdoutBinary
.stdout_() =
598 OCTETSTRING(stdout_buffer
.get_read_len(), stdout_buffer
.get_read_data());
599 stdout_buffer
.clear();
600 incoming_message(message_PStdoutBinary
);
603 message_PStdout
.stdout_() =
604 CHARSTRING(stdout_buffer
.get_read_len(), (const char*)stdout_buffer
.get_read_data());
605 stdout_buffer
.clear();
606 incoming_message(message_PStdout
);
608 // incoming_message(message);
613 /***************************
614 * Send stderr msg to TTCN
615 ***************************/
616 void PIPEasp__PT::sendStderr() {
617 if (disableSend
) return;
619 PIPEasp__Types::ASP__PStderr message_PStderr
;
620 PIPEasp__Types::ASP__PStderrBinary message_PStderrBinary
;
621 if (lineMode
&& !binaryMode
) {
622 // send complete lines from buffer
623 const unsigned char* pos
= stderr_buffer
.get_read_data();
624 for(unsigned int i
=0; i
<stderr_buffer
.get_read_len(); i
++) {
626 if (pos
[i
] != '\n') {
631 // length of data is i (+1 is for \n and is not sent)
632 message_PStderr
.stderr_() = CHARSTRING(i
, (const char*)pos
);
635 incoming_message(message_PStderr
);
637 // remove the complete line from buffer,
638 // also set i and pos to the beginning of buffer
639 stderr_buffer
.set_pos(i
+1);
642 pos
= stderr_buffer
.get_read_data();
645 // lineMode false or binaryMode true
647 message_PStderrBinary
.stderr_() =
648 OCTETSTRING(stderr_buffer
.get_read_len(), stderr_buffer
.get_read_data());
649 stderr_buffer
.clear();
650 incoming_message(message_PStderrBinary
);
653 message_PStderr
.stderr_() =
654 CHARSTRING(stderr_buffer
.get_read_len(), (const char*)stderr_buffer
.get_read_data());
655 stderr_buffer
.clear();
656 incoming_message(message_PStderr
);
658 // incoming_message(message);
663 /***************************
664 * Send exitcode msg to TTCN
665 ***************************/
666 void PIPEasp__PT::sendExitCode() {
667 if (disableSend
) return;
669 log("Sending ExitCode to TTCN");
670 PIPEasp__Types::ASP__PExit message_PExit
;
671 message_PExit
.code() = processExitCode
;
672 incoming_message(message_PExit
);
676 /***************************
677 * Send error msg to TTCN
678 ***************************/
679 void PIPEasp__PT::sendError(const char* error_msg
) {
680 PIPEasp__Types::ASP__PError message_PError
;
681 message_PError
.errorMessage() = error_msg
;
682 incoming_message(message_PError
);
686 /***************************
687 * Send Result msg to TTCN
688 ***************************/
689 void PIPEasp__PT::sendResult() {
690 if (!disableSend
) return; // do not send result if process was started by PExecuteBackground
692 log("Sending result to TTCN...");
693 PIPEasp__Types::ASP__PResult message_PResult
;
694 PIPEasp__Types::ASP__PResultBinary message_PResultBinary
;
696 message_PResultBinary
.stdout_() =
697 OCTETSTRING(stdout_buffer
.get_read_len(), stdout_buffer
.get_read_data());
698 message_PResultBinary
.stderr_() =
699 OCTETSTRING(stderr_buffer
.get_read_len(), stderr_buffer
.get_read_data());
700 message_PResultBinary
.code() = processExitCode
;
701 incoming_message(message_PResultBinary
);
703 int messageLen
= stdout_buffer
.get_read_len();
704 const char* messageData
= (const char*)stdout_buffer
.get_read_data();
706 if (lineMode
&& messageData
[messageLen
-1]=='\n') {
707 messageLen
--; // remove newline from the end
710 message_PResult
.stdout_() = CHARSTRING(messageLen
, messageData
);
712 messageLen
= stderr_buffer
.get_read_len();
713 messageData
= (const char*)stderr_buffer
.get_read_data();
715 if (lineMode
&& messageData
[messageLen
-1]=='\n') {
716 messageLen
--; // remove newline from the end
719 message_PResult
.stderr_() = CHARSTRING(messageLen
, messageData
);
720 message_PResult
.code() = processExitCode
;
721 incoming_message(message_PResult
);
724 // clearing the buffers
725 stdout_buffer
.clear();
726 stderr_buffer
.clear();
727 //incoming_message(message);
734 void PIPEasp__PT::log(const char *fmt
, ...)
736 TTCN_Logger::begin_event(TTCN_DEBUG
);
737 TTCN_Logger::log_event("PIPE test port (%s): ", get_name());
740 TTCN_Logger::log_event_va_list(fmt
, ap
);
742 TTCN_Logger::end_event();