Code:
/* PIPES HANDLING CLASS
* written by eXeCuTeR, 19.8.08
* Works only on GNU/Linux or any UNIX system.
* DESCRIPTION:
* This class is used for pipes handling between a parent process and a child process, or between threads in a process,
* it is very easy to use and maintain.
*
* Creating a new instance of the class:
* Pipe *inst = new Pipe(1024);
*
* Creating a new child:
* inst->create_child("ls", my_argument_list);
* (my_argument_list is an array of char pointers (char *args[]) which holds the arguments of the process if there's any.
* note that the last node of the array must be NULL, e.g: char *my_args[] = { "ps", "-e", NULL }; otherwise it will fail.
* if there's no arguments, just pass NULL.
*
* Writing and reading data from the pipe:
* inst->Write("Hello world!");
* cout << inst->Read();
*
* NOTE: the child's stdin is the reading stream, and his stdout is the writing stream (i.e, when you send data from the parent process using Write(),
* it will be also put in the child's stdin. Also, when you printf/cout (or sending data to the stdout) in the child process, you are actually sending data to the pipe
* which can be read in the parent process.
*
* Happy pipping!
*/
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cstring>
#include <cassert>
#define CRLF "\r\n"
#define NEWLINE "\n"
using namespace std;
class Pipe
{
private:
int *pipe_fds, buffer_size, temp_size;
char *data, *temp_buffer, *temp_data;
enum { READING, WRITING };
int ReadyToRead(); // specifies CRLFs in the end of the stream
int cut_crlf(char *str); // cut CRLFs from a string
public:
Pipe(int size); // creates the pipe with a specific size of buffer
void create_child(const char *cmd, const char **arguments); // creates a child process
char *Read(); // reads data from the stream (returns the data that was read from the pipe, or NULL on failure)
int Write(const char *data); // sends data to the stream (returns the numbers of bytes that were written, or -1 on failure)
inline int get_pipe_r() { return this->pipe_fds[READING]; } // returns the reading pipe
inline int get_pipe_w() { return this->pipe_fds[WRITING]; } // returns the writing pipe
~Pipe(); // frees resources
};
int main(void)
{
Pipe *a = new Pipe(11);
a->create_child("child process", NULL);
/*
* the child process contains:
char buffer[256];
fgets(buffer, sizeof(buffer), stdin); // this will be read from the pipe
fputs("Hey, how you doing?\n", stdout); // this will be sent to the pipe
*/
a->Write("Hello world"); // this will be entered to buffer because fgets is blocking until some data is in the stdin stream
a->Write("Hey, thanks!"); // this will be entered to the stream
char *data = a->Read(); // the data is received from the pipe
cout << data;
return 0;
}
Pipe::Pipe(int size)
{
pipe_fds = new int[2];
pipe(this->pipe_fds);
assert(size > 10);
this->buffer_size = size;
this->data = new char[this->buffer_size];
this->temp_buffer = new char[this->buffer_size];
this->temp_data = new char[this->buffer_size];
this->temp_size = this->buffer_size;
memset(this->temp_buffer, '\0', this->buffer_size);
memset(this->data, '\0', this->buffer_size);
memset(this->temp_data, '\0', this->buffer_size);
}
void Pipe::create_child(const char *cmd, const char **arguments)
{
pid_t fork_ret = fork(); // creating a new child
if(fork_ret == (pid_t)0) // this is the child
{
assert(cmd != NULL);
dup2(this->pipe_fds[READING], STDIN_FILENO); // the child's stdin stream is now redirected to the reading stream
dup2(this->pipe_fds[WRITING], STDOUT_FILENO); // the child's stdout stream is now redirected to the writing stream
execv(cmd, (char * const *)arguments); // executing the child
cerr << "An error occurred - could not run child process.\n"; // in case execv failed
exit(1);
}
}
char *Pipe::Read()
{
if(this->ReadyToRead() == 0)
{
cerr << "Couldn't read from the stream.\n";
return NULL;
}
else
{
/* Reading from the stream */
unsigned int bytes_read = 0;
int temp = 0;
char *bytes = new char[this->buffer_size];
memset(bytes, '\0', this->buffer_size);
do
{
temp = read(this->pipe_fds[READING], this->temp_buffer, this->buffer_size);
if(temp == -1)
return NULL; // error
bytes_read += temp;
if(bytes_read > (unsigned int)this->temp_size)
{
this->temp_size += bytes_read;
bytes = (char *)realloc(bytes, this->temp_size); // allocating more space for the rest of the data
}
if(strstr(this->temp_buffer, CRLF) != NULL)
{
strcat(bytes, this->temp_buffer);
break;
}
strcat(bytes, this->temp_buffer); // adding the new buffer to the data
} while(strstr(this->temp_buffer, CRLF) == NULL);
bytes_read = this->cut_crlf(bytes);
if( (bytes_read - this->buffer_size) > 0)
{
this->temp_data = (char *)realloc(this->temp_data, (strlen(bytes) - sizeof(this->temp_data)));
memset(this->temp_data, '\0', sizeof(this->temp_data));
}
strcpy(this->temp_data, bytes);
return this->temp_data;
}
}
int Pipe::Write(const char *data)
{
/* Writing to the stream */
char *buffer = new char[strlen(data) + strlen(NEWLINE)];
strcat(buffer, data);
strcat(buffer, NEWLINE);
unsigned int bytes_written = 0;
int temp = 0;
do
{
temp = write(this->pipe_fds[WRITING], buffer + bytes_written, strlen(buffer + bytes_written));
if(temp == -1)
return -1;
bytes_written += temp;
} while(bytes_written < (strlen(data) + strlen(NEWLINE)));
return bytes_written;
}
int Pipe::cut_crlf(char *str)
{
unsigned int i, k = 0;
for(i = 0; i < strlen(str) - 1; i++)
{
if(str[i] == '\r' && str[i + 1] == '\n')
memset(str + i, '\b', 2);
else
k++;
}
return k;
}
int Pipe::ReadyToRead()
{
if(write(this->pipe_fds[WRITING], "\r\n\r\n\r\n\r\n\r\n\r\n", strlen("\r\n\r\n\r\n\r\n\r\n\r\n")) == -1)
return 0;
return 1;
}
Pipe::~Pipe()
{
close(this->pipe_fds[READING]);
close(this->pipe_fds[WRITING]);
delete this->pipe_fds;
delete this->temp_buffer;
delete this->data;
}