Skip to content
Snippets Groups Projects
Commit 428f4208 authored by Kostyantyn Vorobyov's avatar Kostyantyn Vorobyov
Browse files

[RTL] Interface for running shell commands

parent a1adf7ec
No related branches found
No related tags found
No related merge requests found
/**************************************************************************/
/* */
/* This file is part of the Frama-C's E-ACSL plug-in. */
/* */
/* Copyright (C) 2012-2015 */
/* CEA (Commissariat à l'énergie atomique et aux énergies */
/* alternatives) */
/* */
/* you can redistribute it and/or modify it under the terms of the GNU */
/* Lesser General Public License as published by the Free Software */
/* Foundation, version 2.1. */
/* */
/* It is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Lesser General Public License for more details. */
/* */
/* See the GNU Lesser General Public License version 2.1 */
/* for more details (enclosed in the file license/LGPLv2.1). */
/* */
/**************************************************************************/
/*! ***********************************************************************
* \file e_acsl_shexec.h
* \brief Interface for running shell commands
***************************************************************************/
#ifndef E_ACSL_SHEXEC
#define E_ACSL_SHEXEC
#include "e_acsl_malloc.h"
#include "e_acsl_printf.h"
#include "e_acsl_string.h"
#include <errno.h>
#include <sys/wait.h>
/*! \class ipr_t
* \brief Result struct for `shexec` function -- execute a command in the
* shell via fork/exec and return results */
typedef struct {
/** \brief resulting STDERR stream as \p const \p char* */
char *stderrs;
/** \brief Supplied STDIN stream as \p const \p char* */
char *stdins;
/** \brief resulting STDOUT stream as \p const \p char* */
char *stdouts;
/** \brief Exit status of a program */
int exit_status;
/** \brief ID of a child process this command has been executed in */
pid_t pid;
/** \brief Set to non-zero if child process is interrupted via a signal */
int signaled;
/** \brief If \p signalled is set, \p signo is set to the number of signal
* that interrupted execution of a child process */
int signo;
/** \brief A command to execute. Needs to be NULL terminated */
char **argv; /** \brief ARGV */
/** \brief Message if the command has failed to run */
char *error;
} ipr_t;
/* \brief Read characters from a buffer associated with a file descriptor into
* a C string
*
* Read a string from a buffer associated with a file descriptor by allocating
* a string of an initial size and increasing the size of a buffer with
* realloc. This is for the cases when we can not quickly seek through the
* file and identify the size of a buffer associated with a file descriptor
*
* \param fd - file descriptor to read from
* \param bufsize - the number of characters we read a t a time
*
* \return NUL-terminated C string on success
* \return NULL on failure */
char* fd_read (int fd, short bufsize) {
/* Read `buffer_size` chars at a time */
short buffer_size = bufsize*sizeof(char);
/* Size of the fetched string */
int size = buffer_size;
/* Buffer where for read data */
char *buffer = (char*)native_malloc(size);
/* The number of read bytes */
short fetched = 0;
int rd = 0; /* Count of fetched characters */
/* Each time the pointer if moved by `size - buffer_size` as initially
* the size of 'buffer' is 'buffer_size' */
while ((fetched = read(fd, buffer + size - buffer_size, buffer_size))) {
rd += fetched;
if (fetched != -1) {
size += fetched*sizeof(char);
buffer = native_realloc(buffer, size + 1);
} else {
return NULL;
}
}
buffer[rd] = '\0';
return buffer;
}
/* Execute a command in the shell and place results to data */
ipr_t* __shexec (ipr_t *data) {
int outfd[2], errfd[2], infd[2];
int oldstdout, oldstderr, oldstdin;
if (pipe(infd)) /* From where parent is going to read */
data->error = nstrdup("Can not create a pipe for STDIN");
if (pipe(outfd)) /* From where parent is going to read */
data->error = nstrdup("Can not create a pipe for STDOUT");
if (pipe(errfd)) /* From where parent is going to read */
data->error = nstrdup("Can not create a pipe for STDERR");
/* Immediately return if reading from one of the STD pipes failed */
if (data->error)
return data;
/* Save stdin, stdout and stderr */
oldstdin = dup(0);
oldstdout = dup(1);
oldstderr = dup(2);
/* Close stdin, stdout and stderr */
close(0);
close(1);
close(2);
dup2(infd[0], 0); /* Make the read end of infd as STDIN */
dup2(outfd[1],1); /* Make the write end of outfd as STDOUT */
dup2(errfd[1],2); /* Make the write end of outfd as STDERR */
pid_t pid = fork();
if(!pid) {
/* Close the streams as they are not required for a child */
close(infd[0]); close(outfd[0]); close(errfd[0]);
close(infd[1]); close(outfd[1]); close(errfd[1]);
execvp(data->argv[0],data->argv);
dup2(oldstderr,2); /* Restore stderr */
if (errno) {
data->error = nstrdup("Failed to execute:\n ");
char **arg = data->argv - 1;
while(*++arg)
data->error = sappend(*arg, data->error, " ");
}
} else {
close(0);
close(1);
close(2);
dup2(oldstdin, 0);
dup2(oldstdout,1);
dup2(oldstderr,2);
close(outfd[1]);
close(errfd[1]);
close(infd[0]);
/* If data->stdin string if supplied, write that string to child's
STDIN first */
if (data->stdins) /* Return NULL if write fails */
if (write(infd[1], data->stdins, strlen(data->stdins)) == -1)
return NULL;
/* Read from child's stdout and stderr */
data->stdouts = fd_read(outfd[0], 256);
if (!data->stdouts)
data->error = nstrdup("Error reading from STDOUT pipe");
data->stderrs = fd_read(errfd[0], 256);
if (!data->stderrs)
data->error = nstrdup("Error reading from STDERR pipe");
/* Close file descriptors we still have open */
close(outfd[0]); /* read end of STDOUT */
close(errfd[0]); /* read end of STDERR */
close(infd[1]); /* write end of STDIN */
int status;
waitpid(pid,&status,0); /* wait for the child to finish */
data->exit_status = WEXITSTATUS(status); /* exis status */
data->pid = pid; /* process number */
data->signaled = WIFSIGNALED(status); /* signal caught */
data->signo = WTERMSIG(status); /* signal number caught */
return data;
}
return NULL;
}
/* \brief Deallocate an `ipr_t` structure returned by `shexec` */
void free_ipr (ipr_t* ipr) {
if (ipr) {
if (ipr->stdouts)
native_free(ipr->stdouts);
if (ipr->stderrs)
native_free(ipr->stderrs);
if (ipr->error)
native_free(ipr->error);
if (ipr->stdins)
native_free(ipr->stdins);
native_free(ipr);
}
}
/* \brief Execute a command given via parameter `data` in the current shell
* and its result as `ipr_t` struct.
*
* \param data - command to execute. This argument is assumed to be a
* NULL-terminated array of strings
* \param sin - if not NULL a C string given via `sin` is supplied as standard
* input to the executed command
* \return - heap-allocated struct `ipr_t` that describes the output of the
* executed command. De-allocation of this struct should be performed via the
* `free_ipr` function */
static ipr_t* shexec (char **data, const char *sin) {
/* Allocate and initialise the struct that we use */
ipr_t *ipr = (ipr_t*)native_malloc(sizeof(ipr_t));
ipr->stderrs = NULL;
ipr->stdouts = NULL;
ipr->stdins = nstrdup(sin);
ipr->argv = data;
ipr->exit_status = 0;
ipr->pid = 0;
ipr->signaled = 0;
return __shexec(ipr);
}
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment