| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // Copyright (c) 2000-2019 Ericsson Telecom AB // |
| // // |
| // All rights reserved. This program and the accompanying materials // |
| // are made available under the terms of the Eclipse Public License v2.0 // |
| // which accompanies this distribution, and is available at // |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "EPTF_CLL_Base_Definitions.hh" |
| #include "EPTF_CLL_Base_Functions.hh" |
| #include <sys/types.h> |
| #include <netdb.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <sys/wait.h> |
| #include <ctype.h> |
| #include <sys/stat.h> |
| #include <libgen.h> |
| #include <limits.h> |
| |
| |
| #ifndef PIPE_BUF_SIZE |
| #define PIPE_BUF_SIZE 65534 |
| #endif |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__upcast |
| // |
| // Purpose: |
| // Implementation of the external function f_EPTF_Base_upcast. |
| /////////////////////////////////////////////////////////// |
| INTEGER EPTF__CLL__Base__Functions::f__EPTF__Base__upcast(const EPTF__CLL__Base__Definitions::EPTF__Base__CT__private& pl__compRef) { |
| return INTEGER((component)pl__compRef); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__downcast |
| // |
| // Purpose: |
| // Implementation of the external function f_EPTF_Base_downcast. |
| /////////////////////////////////////////////////////////// |
| EPTF__CLL__Base__Definitions::EPTF__Base__CT EPTF__CLL__Base__Functions::f__EPTF__Base__downcast(const INTEGER& pl__baseCompRef) { |
| return EPTF__CLL__Base__Definitions::EPTF__Base__CT(pl__baseCompRef); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__assert |
| // |
| // Purpose: |
| // Implementation of the external function f_EPTF_Base_assert. |
| /////////////////////////////////////////////////////////// |
| void EPTF__CLL__Base__Functions::f__EPTF__Base__assert(const CHARSTRING& pl__assertMessage, const BOOLEAN& pl__predicate) |
| { |
| #ifdef EPTF_DEBUG |
| if (!(pl__predicate)) { |
| f__EPTF__Base__addAssertMsg(pl__assertMessage); |
| TTCN_Logger::log_str(TTCN_Logger::ERROR_UNQUALIFIED, CHARSTRING("f_EPTF_Base_assert: Assertion failed! ")+pl__assertMessage); |
| if (EPTF__CLL__Base__Definitions::EPTF__Base__CT__private_component_v__EPTF__Base__negativeTestMode==true) { |
| EPTF__CLL__Base__Functions::f__EPTF__Base__stop(NONE); |
| } else { |
| EPTF__CLL__Base__Functions::f__EPTF__Base__stop(FAIL); |
| } |
| //EPTF__CLL__Base__Functions::f__EPTF__Base__stopAll(EPTF__CLL__Base__Functions::f__EPTF__Base__stopAll_pl__noCleanup_defval); |
| } |
| #endif |
| } |
| |
| FLOAT EPTF__CLL__Base__Functions::f__EPTF__Base__getTimeOfDay() |
| { |
| struct timeval tv; |
| gettimeofday(&tv,NULL); |
| return tv.tv_sec+tv.tv_usec/1000000.0; |
| } |
| |
| INTEGER EPTF__CLL__Base__Functions::f__EPTF__Base__getPid() |
| { |
| pid_t myPid = getpid(); |
| return (unsigned int)myPid; |
| } |
| |
| // egbotat |
| CHARSTRING EPTF__CLL__Base__Functions::f__EPTF__Base__getHostName() |
| { |
| char buf[1024]; |
| int res = gethostname(buf, sizeof(buf)-1); |
| if(res) { |
| TTCN_Logger::log(TTCN_Logger::ERROR_UNQUALIFIED, "gethostname failed with error code %d, h_errno: %d (%s)", |
| res, h_errno, hstrerror(h_errno)); |
| return CHARSTRING(""); |
| } |
| return CHARSTRING(buf); |
| } |
| //~egbotat |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__system |
| // |
| // Purpose: |
| // Implementation of the external function f__EPTF__Base__system. |
| /////////////////////////////////////////////////////////// |
| INTEGER EPTF__CLL__Base__Functions::f__EPTF__Base__system(const CHARSTRING& pl__command) { |
| return system(pl__command); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__executeShell |
| // |
| // Purpose: |
| // Implementation of the external function f_EPTF_Base_executeShell. |
| /////////////////////////////////////////////////////////// |
| INTEGER EPTF__CLL__Base__Functions::f__EPTF__Base__executeShell(const CHARSTRING& pl__command, CHARSTRING& pl__stdOut, CHARSTRING& pl__stdErr, const BOOLEAN& pl__enableAlts) |
| { |
| pl__stdOut=""; |
| pl__stdErr=""; |
| const char* cmd=pl__command; |
| |
| int pipesStdin[2]; |
| int pipesStdout[2]; |
| int pipesStderr[2]; |
| int processPid; |
| |
| // create pipes |
| if (pipe(pipesStdin) != 0) { |
| return -1; |
| } |
| if (pipe(pipesStdout) != 0) { |
| return -1; |
| } |
| if (pipe(pipesStderr) != 0) { |
| return -1; |
| } |
| |
| fd_set select_set; |
| FD_ZERO(&select_set); |
| FD_SET(pipesStdout[0],&select_set); |
| FD_SET(pipesStderr[0],&select_set); |
| int max_fd=0; |
| max_fd=max_fd>pipesStdout[0]?max_fd:pipesStdout[0]; |
| max_fd=max_fd>pipesStderr[0]?max_fd:pipesStderr[0]; |
| |
| processPid = fork(); |
| if (processPid == -1) { |
| // close all pipes |
| close(pipesStdin[0]); |
| close(pipesStdin[1]); |
| close(pipesStdout[0]); |
| close(pipesStdout[1]); |
| close(pipesStderr[0]); |
| close(pipesStderr[1]); |
| TTCN_Logger::log(TTCN_Logger::ERROR_UNQUALIFIED, "Cannot fork"); |
| exit(errno); |
| } |
| |
| if (processPid == 0) { // created child process |
| TTCN_Logger::log(TTCN_Logger::TTCN_DEBUG,"f_EPTF_Base_executeShell: Executing command: %s",(const char*)pl__command); |
| |
| // close the parent end of the pipes |
| close(pipesStdin[1]); |
| close(pipesStdout[0]); |
| close(pipesStderr[0]); |
| |
| // redirect pipeStdin to stdin |
| int r; |
| r = dup2(pipesStdin[0], STDIN_FILENO); |
| if (r<0) { |
| exit(errno); |
| } |
| // redirect pipeStdout to stdout |
| r = dup2(pipesStdout[1], STDOUT_FILENO); |
| if (r<0) { |
| exit(errno); |
| } |
| // redirect pipeStderr to stderr |
| r = dup2(pipesStderr[1], STDERR_FILENO); |
| if (r<0) { |
| exit(errno); |
| } |
| |
| // parse for arguments |
| char* argv[4]; |
| // following two commented part is needed when no tcsh used to execute |
| /* int argc = 0; |
| CHARSTRING temp = ""; |
| |
| for(int i = 0; cmd[i] != 0; i++) { |
| if (isspace(cmd[i])) { |
| argv[argc++] = strdup(temp); |
| while (cmd[i] != '0' && isspace(cmd[i])) i++; |
| i--; |
| temp = ""; |
| } else { |
| temp = temp + CHARSTRING(1, cmd+i); |
| } |
| } |
| if(temp != "") { |
| argv[argc++] = strdup(temp); |
| } |
| argv[argc++] = (char*)NULL; // no additional args |
| */ |
| argv[0]=strdup("tcsh"); |
| argv[1]=strdup("-c"); |
| argv[2]=strdup(cmd); |
| argv[3]=NULL; |
| |
| // execute |
| if(execvp(argv[0],argv) < 0) { |
| fprintf(stderr,"f_EPTF_Base_executeShell: Failed to start the %s",(const char*)argv[0]); |
| fflush(stdout); |
| fflush(stderr); |
| /* |
| argv[0]="tcsh"; |
| argv[1]="-c"; |
| argv[2]=(char*)(const char*)(CHARSTRING("exit ")+int2str(errno)); |
| argv[3]=NULL; |
| if(execvp(argv[0],argv)< 0) { |
| fprintf(stderr,"f_EPTF_Base_executeShell: Failed to start the %s",(const char*)argv[0]); |
| } |
| */ |
| } |
| TTCN_Logger::log(TTCN_Logger::TTCN_DEBUG,"EXECVP FINISHED:"); |
| // this is never reached: 2nd execvp is always successful |
| |
| } else { // Parent process |
| TTCN_Logger::log(TTCN_Logger::TTCN_DEBUG,"f_EPTF_Base_executeShell: %s launched with PID: %i",(const char*)pl__command, processPid); |
| // close child end of the pipes |
| close(pipesStdin[0]); |
| close(pipesStdout[1]); |
| close(pipesStderr[1]); |
| |
| char buffer[PIPE_BUF_SIZE]; |
| bool finishedout = false; |
| bool finishederr = false; |
| struct timeval tv; |
| while(!(finishedout&&finishederr)) { |
| if(pl__enableAlts) { |
| f__EPTF__Base__waitForCondition(f__EPTF__Base__waitForCondition_pl__checkCondFn_defval, 1.0e-2); |
| } |
| FD_ZERO(&select_set); |
| if(!finishedout){ |
| FD_SET(pipesStdout[0],&select_set); |
| } |
| if(!finishederr){ |
| FD_SET(pipesStderr[0],&select_set); |
| } |
| tv.tv_sec = 0; |
| tv.tv_usec = 1000; |
| int result = select(max_fd+1,&select_set,NULL,NULL,&tv); |
| if(result<0){ |
| if(errno != EINTR) { |
| close(pipesStdin[1]); |
| close(pipesStdout[0]); |
| close(pipesStderr[0]); |
| return errno; |
| } |
| } else { |
| if(pipesStdout[0]!=-1 && FD_ISSET(pipesStdout[0],&select_set)){ |
| int nBytes = PIPE_BUF_SIZE; |
| int r = read(pipesStdout[0],buffer,(int)nBytes); |
| if(r>0){ |
| pl__stdOut += CHARSTRING(r,buffer); |
| } else { |
| // child process exited |
| finishedout = true; |
| FD_CLR(pipesStdout[0],&select_set); |
| close(pipesStdout[0]); |
| pipesStdout[0]=-1; |
| } |
| } else if(pipesStderr[0]!=-1 && FD_ISSET(pipesStderr[0],&select_set)){ |
| int nBytes = PIPE_BUF_SIZE; |
| int r = read(pipesStderr[0],buffer,(int)nBytes); |
| if(r>0){ |
| pl__stdErr += CHARSTRING(r,buffer); |
| } else { |
| // child process exited |
| finishederr = true; |
| FD_CLR(pipesStderr[0],&select_set); |
| close(pipesStderr[0]); |
| pipesStderr[0]=-1; |
| } |
| } |
| } |
| } |
| |
| int processExitCode=0; |
| TTCN_Logger::log(TTCN_Logger::TTCN_DEBUG, "f_EPTF_Base_executeShell: Waiting for child with PID: %d", processPid); |
| waitpid(processPid,&processExitCode,0); |
| processExitCode>>=8; |
| |
| TTCN_Logger::log(TTCN_Logger::TTCN_DEBUG, "f_EPTF_Base_executeShell: Child process exit status is: %d", processExitCode); |
| |
| close(pipesStdin[1]); |
| close(pipesStdout[0]); |
| close(pipesStderr[0]); |
| |
| return processExitCode; |
| } |
| return -1; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__getStartCmd |
| // |
| // Purpose: |
| // Implementation of the external function f_EPTF_Base_getStartCmd |
| // |
| // Description: |
| // Returns the executable name from /proc/self/exe. |
| // The return value can be perceived as the basename of the value in argv[0] inside main(), as far as argv[0] represents the executable name on the given platform. |
| // |
| // Readme: |
| // http://linux.die.net/man/2/readlink |
| // http://linux.die.net/man/3/basename |
| /////////////////////////////////////////////////////////// |
| CHARSTRING EPTF__CLL__Base__Functions::f__EPTF__Base__getStartCmd() |
| { |
| char path[20]; |
| char linkname[_POSIX_ARG_MAX+1]; // Use the stack, not the heap. |
| char *executable; // Do not free() me. |
| |
| snprintf(path, 20, "/proc/%d/exe", getppid()); |
| size_t linkname_size = readlink(path, linkname, _POSIX_ARG_MAX); |
| if (linkname_size <= 0) // on error, fail silently. |
| { |
| return CHARSTRING(""); |
| } |
| linkname[linkname_size] = 0; // "readlink() does not append a null byte to buf." |
| executable = basename(linkname); // basename manages it's own memory allocations, do not free(). |
| return CHARSTRING(executable); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__getStartDir |
| // |
| // Purpose: |
| // Implementation of the external function f_EPTF_Base_getStartDir |
| // |
| // Description: |
| // Returns the directory name from /proc/self/exe. |
| // The return value can be perceived as the dirname of the value in argv[0] inside main(), as far as argv[0] represents the executable name on the given platform. |
| // |
| // Readme: |
| // http://linux.die.net/man/2/readlink |
| // http://linux.die.net/man/3/dirname |
| /////////////////////////////////////////////////////////// |
| CHARSTRING EPTF__CLL__Base__Functions::f__EPTF__Base__getStartDir() |
| { |
| char path[20]; |
| char linkname[_POSIX_ARG_MAX+1]; // Use the stack, not the heap. |
| char *dir; // Do not free() me. |
| |
| snprintf(path, 20, "/proc/%d/exe", getppid()); |
| size_t linkname_size = readlink(path, linkname, _POSIX_ARG_MAX); |
| if (linkname_size <= 0) // on error, fail silently. |
| { |
| return CHARSTRING(""); |
| } |
| linkname[linkname_size] = 0; // "readlink() does not append a null byte to buf." |
| dir = dirname(linkname); // dirname manages it's own memory allocations, do not free(). |
| return CHARSTRING(dir); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__get_process_parent_id |
| // |
| // Purpose: |
| // Helper for f__EPTF__Base__getEffectiveCmdline. It returns the parentPID of an arbitrary PID. |
| // |
| // Description: |
| // On error, the output remains the same. |
| // BUFSIZ - stdio.h |
| // |
| // Readme: |
| // http://man7.org/linux/man-pages/man5/proc.5.html section /proc/[pid]/stat |
| /////////////////////////////////////////////////////////// |
| static inline void f__EPTF__Base__get_process_parent_id(const pid_t pid, pid_t * ppid) |
| { |
| char buffer[BUFSIZ]; |
| FILE *f; |
| |
| sprintf(buffer, "/proc/%d/stat", pid); |
| f = fopen(buffer, "r"); |
| |
| if (f) |
| { |
| if (fread(buffer, sizeof(char), sizeof(buffer), f) > 0) |
| { |
| strtok(buffer, " "); // (1) pid %d |
| strtok(NULL, " "); // (2) comm %s |
| strtok(NULL, " "); // (3) state %c |
| char *s_ppid = strtok(NULL, " "); // (4) ppid %d |
| if (s_ppid) |
| { |
| *ppid = atoi(s_ppid); |
| } else { |
| *ppid = 0; |
| } |
| } |
| fclose(f); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__get_cmdline_from_file |
| // |
| // Purpose: |
| // Helper for f__EPTF__Base__getEffectiveCmdline. It returns the command line as an array from procfs. |
| // |
| // Description: |
| // On error, returns an empty charstringlist. |
| // _POSIX_ARG_MAX - limits.h SUSv2 |
| // |
| // Readme: |
| // http://man7.org/linux/man-pages/man5/proc.5.html section /proc/[pid]/cmdline |
| /////////////////////////////////////////////////////////// |
| static inline EPTF__CLL__Common__Definitions::EPTF__CharstringList f__EPTF__Base__get_cmdline_from_file(FILE *f) |
| { |
| EPTF__CLL__Common__Definitions::EPTF__CharstringList retVal(NULL_VALUE); |
| char arg[_POSIX_ARG_MAX]; // string builder for the current argument |
| int a = 0, r = 0, c, i = 1; // arg idx, retVal idx, char from getc, arg size idx |
| |
| memset(arg, 0, _POSIX_ARG_MAX); |
| while ((c = fgetc(f)) != EOF) |
| { |
| if (c && i < _POSIX_ARG_MAX) // c is not a null character AND the current arg is not longe rthan the buffer size |
| { |
| arg[a++] = c; |
| i++; |
| } else { // c is \0, we reached the end of the current arg in the list OR the current arg reached buffer size |
| retVal[r++] = CHARSTRING(arg); |
| a = 0; |
| i = 1; // it starts by 1, so we don't have to play with the buffer sizes |
| memset(arg, 0, _POSIX_ARG_MAX); |
| } |
| } |
| retVal[r] = CHARSTRING(arg); |
| return retVal; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f__EPTF__Base__getEffectiveCmdline |
| // |
| // Purpose: |
| // Implementation of the external function f_EPTF_Base_getEffectiveCmdline |
| // |
| // Description: |
| // The /proc/[pid]/cmdline file is zero-separated between the arguments. |
| /////////////////////////////////////////////////////////// |
| EPTF__CLL__Common__Definitions::EPTF__CharstringList EPTF__CLL__Base__Functions::f__EPTF__Base__getEffectiveCmdline() |
| { |
| char path[20]; // path for the cmdline pseudofile |
| char expect[7]; |
| pid_t pid = getpid(); // pid for the parent discovery |
| FILE *f; |
| EPTF__CLL__Common__Definitions::EPTF__CharstringList retVal(NULL_VALUE); |
| |
| do { |
| snprintf(path, 20, "/proc/%d/cmdline", pid); |
| f = fopen(path, "rb"); |
| if (f) |
| { |
| fread(expect, sizeof(char), 7, f); |
| if (!strncmp(expect, "expect", 6)) // is this the parent process what we expect? |
| { |
| retVal = f__EPTF__Base__get_cmdline_from_file(f); |
| fclose(f); |
| return retVal; |
| } else if (!strncmp(expect, "mctr_gu", 7)) { // mctr_gui, we hope. In this case, there is no "expect". |
| TTCN_Logger::log(TTCN_Logger::TTCN_DEBUG, "f__EPTF__Base__getEffectiveCmdline: mctr_gui found in process %i, which does not provide command line, sorry.", pid); |
| fclose(f); |
| break; |
| } else { |
| f__EPTF__Base__get_process_parent_id(pid, &pid); |
| } |
| fclose(f); |
| } else { // Cannot read parent's cmdline for any reason, which is bad |
| break; |
| } |
| } while(pid > 1); // pid 1 is [init], that is the top of the tree. |
| |
| TTCN_Logger::log(TTCN_Logger::TTCN_DEBUG, CHARSTRING("f__EPTF__Base__getEffectiveCmdline: Reached the end, returning with failover.")); |
| return retVal; // default if expect-ed parent not found. |
| } |
| |