shell: add exec functions

This commit is contained in:
Mateo FERON 2020-04-10 15:02:49 +02:00
parent 32c2c3dab8
commit 95c86ba024
2 changed files with 204 additions and 113 deletions

View file

@ -6,6 +6,8 @@
#include <queue>
#include <stdio.h>
#include <thread>
#include "wait.hpp"
@ -15,13 +17,16 @@
namespace ztd
{
/// SHELL CALLS ///
//! @brief Execute a shell command and retrieve its output
/*!
@param command Shell command to execute
@param to_console Output to console
Doesn't output to console
@return Output of command
*/
std::string sh(const std::string& command, bool to_console=false);
std::string sh(const std::string& command);
//! @brief Execute a shell command and retrieve its return value
/*!
@ -34,31 +39,17 @@ namespace ztd
//! @brief Execute a shell command and retrieve its output and return value
/*!
@param command Shell command to execute
@param to_console Output to console
Doesn't output to console
@return @b first Output of command\n@b second Return value of command
*/
std::pair<std::string, int> shp(const std::string& command, bool to_console=false);
//! @brief popen C function with added pid functionality
/*!
@param pid Pointer to an @a int in which the process's pid will be stored
@see <a href="http://man7.org/linux/man-pages/man3/popen.3.html">popen(), pclose()</a>
*/
FILE* popen2(const char* command, const char* type, int* pid);
//! @brief pclose C function with added pid functionality
/*!
@param pid Pid of the opened process
@see <a href="http://man7.org/linux/man-pages/man3/popen.3.html">popen(), pclose()</a>
*/
int pclose2(FILE* fp, pid_t pid);
std::pair<std::string, int> shp(const std::string& command);
//! @brief Shell call class
class shc
{
public:
//! @brief constructor
shc(std::string const& cmd="", bool const cout=false);
shc(std::string const& cmd="");
virtual ~shc();
//! @brief Start the command
@ -68,6 +59,7 @@ namespace ztd
//! @brief Wait until the command gives output
void wait_output();
inline bool has_output() { return this->output.size() > 0; }
//! @brief Retrieve a line from the command's output
std::string get_output();
@ -76,8 +68,6 @@ namespace ztd
//! @brief Command to execute
std::string command;
//! @brief Output the command result to console
bool to_console;
//! @brief Run status of the command
bool running;
@ -92,10 +82,66 @@ namespace ztd
ztd::wait_pool wp_output;
ztd::wait_pool wp_finish;
std::thread thread;
private:
static void run_process(shc* p);
static void run_process(shc* p, ztd::wait_pool* wp);
};
//// POPEN WITH PID ////
//// EXEC EXTENSIONS ////
/// open/close extensions
//! @brief Similar to popen2() but calls an executable instead of a shell
/*!
@param type Mode of the execution (r/w)
@param pid Pointer to an @a int in which the process's pid will be stored
@param bin Binary to execute. Has PATH resolution
@param args
@return File descriptor for the stream in question
*/
FILE* eopen(const char* type, int* pid, const char* bin, std::vector<char*> args);
//! @brief Similar to pclose2() but for eopen()
/*!
@param fd
@param pid Pid of the opened process
@return Return value of the command
*/
int eclose(FILE* fd, pid_t pid);
//! @brief popen C function with added pid functionality
/*!
@param command Shell command to execute
@param type Mode of the execution (r/w)
@param pid Pointer to an @a int in which the process's pid will be stored
@return File descriptor for the stream in question
@see <a href="http://man7.org/linux/man-pages/man3/popen.3.html">popen(), pclose()</a>
*/
inline FILE* popen2(const char* command, const char* type, int* pid) { std::vector<char*> tvec = {"-c",(char*) command}; return eopen(type, pid, "/bin/sh", tvec) ; }
//! @brief pclose C function with added pid functionality
/*!
@param fd
@param pid Pid of the opened process
@return Return value of the command
@see <a href="http://man7.org/linux/man-pages/man3/popen.3.html">popen(), pclose()</a>
*/
inline int pclose2(FILE* fd, pid_t pid) { return eclose(fd, pid); }
/// exec extensions
//! @brief Execute a binary and retrieve its outputs
/*!
@param bin Binary file to execute. Has PATH resolution
@param args Arguments given to the binary
*/
std::pair<std::string, int> exec(std::string const& bin, std::vector<char*> const& args);
//! @brief @see exec(std::string const& bin, std::vector<char*> const& args)
std::pair<std::string, int> exec(std::string const& bin, std::vector<std::string> const& args);
}
#endif //SHELL_HPP

View file

@ -1,6 +1,7 @@
#include "shell.hpp"
#include <thread>
#include <fstream>
#include <unistd.h>
#include <stdlib.h>
@ -9,9 +10,11 @@
#include <errno.h>
#include <signal.h>
std::string ztd::sh(const std::string& command, bool to_console)
//// SHELL CALLS ////
std::string ztd::sh(const std::string& command)
{
return ztd::shp(command, to_console).first;
return ztd::shp(command).first;
}
int ztd::shr(const std::string& command)
@ -19,7 +22,7 @@ int ztd::shr(const std::string& command)
return WEXITSTATUS(system(command.c_str()));
}
std::pair<std::string, int> ztd::shp(const std::string& command, bool to_console)
std::pair<std::string, int> ztd::shp(const std::string& command)
{
std::string ret;
FILE *stream = popen(command.c_str(), "r");
@ -27,100 +30,42 @@ std::pair<std::string, int> ztd::shp(const std::string& command, bool to_console
size_t buff_size = 0;
while (getline(&buff, &buff_size, stream) > 0)
{
if(to_console)
printf("%s", buff);
ret += buff;
}
return std::make_pair(ret, WEXITSTATUS(pclose(stream)));
}
FILE* ztd::popen2(const char* command, const char* type, int* pid)
{
const int READ=0, WRITE=1;
pid_t child_pid;
int fd[2];
pipe(fd);
if((child_pid = fork()) == -1)
{
perror("fork");
exit(1);
}
/* child process */
if (child_pid == 0)
{
if ( index(type, 'r') != NULL )
{
close(fd[READ]); //Close the READ end of the pipe since the child's fd is write-only
dup2(fd[WRITE], 1); //Redirect stdout to pipe
}
else
{
close(fd[WRITE]); //Close the WRITE end of the pipe since the child's fd is read-only
dup2(fd[READ], 0); //Redirect stdin to pipe
}
setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
exit(0);
}
else
{
if ( index(type, 'r') != NULL )
{
close(fd[WRITE]); //Close the WRITE end of the pipe since parent's fd is read-only
}
else
{
close(fd[READ]); //Close the READ end of the pipe since parent's fd is write-only
}
}
if(pid != NULL)
*pid = child_pid;
if ( index(type, 'r') != NULL )
{
return fdopen(fd[READ], "r");
}
return fdopen(fd[WRITE], "w");
}
int ztd::pclose2(FILE* fp, pid_t pid)
{
int stat;
fclose(fp);
while (waitpid(pid, &stat, 0) == -1)
{
if (errno != EINTR)
{
stat = -1;
break;
}
}
return stat;
/* shc method
std::string ret;
ztd::shc r(command);
r.run();
r.wait_finish();
while(r.has_output())
ret += r.get_output();
return std::make_pair(ret, r.return_value);
*/
}
// SHC
ztd::shc::shc(std::string const& cmd, bool const cout)
ztd::shc::shc(std::string const& cmd)
{
this->command=cmd;
this->to_console=cout;
this->running=false;
this->pid=0;
}
ztd::shc::~shc()
{
if(this->running)
this->kill_int();
this->thread.join();
}
void ztd::shc::run()
{
std::thread(ztd::shc::run_process, this).detach();
ztd::wait_pool wait_for_start;
if(!this->running)
this->thread = std::thread(ztd::shc::run_process, this, &wait_for_start);
if(!this->running)
wait_for_start.wait();
}
int ztd::shc::kill_int()
@ -133,7 +78,7 @@ int ztd::shc::kill_int()
void ztd::shc::wait_output()
{
while(this->output.size() <= 0)
while(this->running && !(this->has_output()) )
this->wp_output.wait();
}
@ -155,31 +100,131 @@ void ztd::shc::wait_finish()
this->wp_finish.wait();
}
void ztd::shc::run_process(shc* p)
void ztd::shc::run_process(shc* p, ztd::wait_pool* wp)
{
if(p->running)
return;
p->running = true;
wp->notify_all();
char* buff = NULL;
size_t buff_size = 0;
int pid = 0;
std::vector<char*> args = { "-c", (char*) p->command.c_str() };
// FILE *stream = ztd::eopen("r", &pid, "/bin/sh", args);
FILE *stream = ztd::popen2(p->command.c_str(), "r", &pid);
p->pid = pid;
p->running = true;
std::string ln;
while ( getline(&buff, &buff_size, stream) > 0 ) //retrieve device lines
while ( getline(&buff, &buff_size, stream) > 0 ) //retrieve lines
{
if(p->to_console)
printf("%s", buff);
ln = std::string(buff, buff_size);
p->output.push(ln);
p->output.push(std::string(buff));
p->wp_output.notify_all();
}
p->running = false;
p->wp_finish.notify_all();
p->return_value = WEXITSTATUS(ztd::pclose2(stream, pid));
p->running = false;
p->wp_output.notify_all();
p->wp_finish.notify_all();
}
//// EXEC EXTENTIONS ////
// open/close calls
FILE* ztd::eopen(const char* type, int* pid, const char* bin, std::vector<char*> args)
{
const int READ=0, WRITE=1;
pid_t child_pid;
int fd[2];
pipe(fd);
args.push_back(NULL); // NULL terminated array for execv()
args.insert(args.begin(), (char*) bin); // first arg is name of the exec
// forking
if((child_pid = fork()) == -1)
{
perror("fork");
exit(1);
}
if (child_pid == 0) // child process
{
if ( index(type, 'r') != NULL )
{
close(fd[READ]); //Close the READ end of the pipe since the child's fd is write-only
dup2(fd[WRITE], 1); //Redirect stdout to pipe
}
else
{
close(fd[WRITE]); //Close the WRITE end of the pipe since the child's fd is read-only
dup2(fd[READ], 0); //Redirect stdin to pipe
}
setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh
execvp(bin, args.data());
exit(0);
}
else // main process
{
if ( index(type, 'r') != NULL )
{
close(fd[WRITE]); //Close the WRITE end of the pipe since parent's fd is read-only
}
else
{
close(fd[READ]); //Close the READ end of the pipe since parent's fd is write-only
}
}
if(pid != NULL)
*pid = child_pid;
if ( index(type, 'r') != NULL )
{
return fdopen(fd[READ], "r");
}
return fdopen(fd[WRITE], "w");
}
int ztd::eclose(FILE* fd, pid_t pid)
{
int stat;
fclose(fd);
while (waitpid(pid, &stat, 0) == -1)
{
if (errno != EINTR)
{
stat = -1;
break;
}
}
return stat;
}
// exec calls
// function itself
std::pair<std::string, int> ztd::exec(std::string const& bin, std::vector<char*> const& args)
{
std::string ret;
pid_t pid;
FILE *stream = eopen("r", &pid, bin.c_str(), args);
char* buff = NULL;
size_t buff_size = 0;
while (getline(&buff, &buff_size, stream) > 0)
{
ret += buff;
}
return std::make_pair(ret, WEXITSTATUS(eclose(stream, pid)));
}
// translate to char* call
std::pair<std::string, int> ztd::exec(std::string const& bin, std::vector<std::string> const& args)
{
std::vector<char*> rargs;
for(auto it: args)
rargs.push_back((char*) it.c_str());
return ztd::exec(bin, rargs);
}