#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
#ifndef _XOPEN_SOURCE_EXTENDED
#define _XOPEN_SOURCE_EXTENDED 1
#endif
#include "botan-1.6/include/unix_cmd.h"
#include "botan-1.6/include/parsing.h"
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/select.h>
namespace Enctain {
namespace Botan {
namespace {
void do_exec(const std::vector<std::string>& arg_list,
const std::vector<std::string>& paths)
{
const u32bit args = arg_list.size() - 1;
const char* arg1 = (args >= 1) ? arg_list[1].c_str() : 0;
const char* arg2 = (args >= 2) ? arg_list[2].c_str() : 0;
const char* arg3 = (args >= 3) ? arg_list[3].c_str() : 0;
const char* arg4 = (args >= 4) ? arg_list[4].c_str() : 0;
for(u32bit j = 0; j != paths.size(); j++)
{
const std::string full_path = paths[j] + "/" + arg_list[0];
const char* fsname = full_path.c_str();
execl(fsname, fsname, arg1, arg2, arg3, arg4, NULL);
}
}
}
struct pipe_wrapper
{
int fd;
pid_t pid;
pipe_wrapper() { fd = -1; pid = 0; }
};
u32bit DataSource_Command::read(byte buf[], u32bit length)
{
if(end_of_data())
return 0;
fd_set set;
FD_ZERO(&set);
FD_SET(pipe->fd, &set);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = MAX_BLOCK_USECS;
ssize_t got = 0;
if(select(pipe->fd + 1, &set, 0, 0, &tv) == 1)
{
if(FD_ISSET(pipe->fd, &set))
got = ::read(pipe->fd, buf, length);
}
if(got <= 0)
{
shutdown_pipe();
return 0;
}
return (u32bit)got;
}
u32bit DataSource_Command::peek(byte[], u32bit, u32bit) const
{
if(end_of_data())
throw Invalid_State("DataSource_Command: Cannot peek when out of data");
throw Stream_IO_Error("Cannot peek/seek on a command pipe");
}
bool DataSource_Command::end_of_data() const
{
return (pipe) ? false : true;
}
int DataSource_Command::fd() const
{
if(!pipe)
return -1;
return pipe->fd;
}
std::string DataSource_Command::id() const
{
return "Unix command: " + arg_list[0];
}
void DataSource_Command::create_pipe(const std::string& path)
{
const std::vector<std::string> paths = split_on(path, ':');
bool found_something = false;
for(u32bit j = 0; j != paths.size(); j++)
{
const std::string full_path = paths[j] + "/" + arg_list[0];
if(access(full_path.c_str(), X_OK) == 0)
{
found_something = true;
break;
}
}
if(!found_something)
return;
int pipe_fd[2];
if(::pipe(pipe_fd) != 0)
return;
pid_t pid = fork();
if(pid == -1)
{
close(pipe_fd[0]);
close(pipe_fd[1]);
}
else if(pid > 0)
{
pipe = new pipe_wrapper;
pipe->fd = pipe_fd[0];
pipe->pid = pid;
close(pipe_fd[1]);
}
else
{
if(dup2(pipe_fd[1], STDOUT_FILENO) == -1)
exit(127);
if(close(pipe_fd[0]) != 0 || close(pipe_fd[1]) != 0)
exit(127);
if(close(STDERR_FILENO) != 0)
exit(127);
do_exec(arg_list, paths);
exit(127);
}
}
void DataSource_Command::shutdown_pipe()
{
if(pipe)
{
pid_t reaped = waitpid(pipe->pid, 0, WNOHANG);
if(reaped == 0)
{
kill(pipe->pid, SIGTERM);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = KILL_WAIT;
select(0, 0, 0, 0, &tv);
reaped = waitpid(pipe->pid, 0, WNOHANG);
if(reaped == 0)
{
kill(pipe->pid, SIGKILL);
do
reaped = waitpid(pipe->pid, 0, 0);
while(reaped == -1);
}
}
close(pipe->fd);
delete pipe;
pipe = 0;
}
}
DataSource_Command::DataSource_Command(const std::string& prog_and_args,
const std::string& path) :
MAX_BLOCK_USECS(100000), KILL_WAIT(10000)
{
arg_list = split_on(prog_and_args, ' ');
if(arg_list.size() == 0)
throw Invalid_Argument("DataSource_Command: No command given");
if(arg_list.size() > 5)
throw Invalid_Argument("DataSource_Command: Too many args");
pipe = 0;
create_pipe(path);
}
DataSource_Command::~DataSource_Command()
{
if(!end_of_data())
shutdown_pipe();
}
}
}