blob: 74b1ff96255745b527026c89061b024ff8268797 [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// //
// 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.
}