Pstream class

I have added a new class to AXCEL (http://sourceforge.net/projects/libaxcel/). The Pstream class allows you to execute a new program and read from and write to its standard input and output. A very powerful mechanism for IPC, this should be useful for webservers. This example code will create a com file on 32-bit windows using debug.exe:

#include "axcel.h"
using namespace axcel;

int main() {
	Pstream p;
	p.open("C:/Windows/system32/debug.exe");
	p <<
		"a\r\n"
		"mov ah,02\r\n"
		"mov dl,41\r\n"
		"int 21\r\n"
		"int 20\r\n\r\n"
		"r cx\r\n"
		"8\r\n"
		"n a.com\r\n"
		"w\r\n"
		"q\r\n";
	con.echo("Begin debug.exe output {");
	con.write(p.dump());
	p.close();
	con.echo("\n} End debug.exe output");
}

For Linux this is implemented by creating 2 pairs of pipes. 1 pair for the parent to write to child’s stdin (and for the child to read from it), and another for the child to write to its stdout (and for the parent to read from it). After a call to fork, the child will duplicate file descriptor 0 to the read end of one pipe pair, and file descriptor 1 to the write end of the other pipe pair. Now as the child reads/writes to one end of the pipe, the parent can read/write to the other end of the pipe.

bool Pstream::open(const char* program, const char* argv, void* envp) {
	String gay = program;
	if (argv) gay = gay.cat(' ').cat(argv);
	wordexp_t we;
	char **w;
	wordexp(gay, &we, WRDE_NOCMD);
	w = we.we_wordv;
	char *const e[] = {NULL};
	if (!fexist(program)) return false;
	pname = program;
	pipe(rpipe);
	pipe(wpipe);
	p = fork();
	if (!p) {
		::close(rpipe[0]);
		::close(wpipe[1]);
		dup2(wpipe[0], 0); ::close(wpipe[0]);
		dup2(rpipe[1], 1); ::close(rpipe[1]);
		if (!envp) execve(program, w, e);
		else execve(program, w, (char* *const)envp);
		wordfree(&we);
	} else {
		wordfree(&we);
		::close(wpipe[0]);
		::close(rpipe[1]);
		return true;
	}
}

As for Windows, the CreateProcess function allows you to specify a STARTUPINFO structure which, among other things, has options for setting the stdin, stdout, and stderr of the process to start. I set that to the appropriate handle that’s part of a pipe created with CreatePipe and set bInheritHandles to true when calling CreateProcess.

bool Pstream::open(const char* program, const char* argv, void* envp) {
	if (!fexist(program)) return false;
	pname = program;
	String args = program;
	File f;
	String s;
	f.open(program, "rb");
	if (f.getc() == '#')
		if (f.getc() == '!') {
			s = f.gets().strip();
			pname = s;
			if (pname.chr(' ') != -1) {
				pname = pname.burn(' ');
				args = pname;
				args += ' ';
				args += s.slurp(' ');
				args += ' ';
				args += program;
			}
		}
	f.close();
	if (argv) args = args.cat(' ').cat(argv);
	con << pname << ' ' << args << '\n';
	CreatePipe(&rin, &win, NULL, 0);
	CreatePipe(&rout, &wout, NULL, 0);
	CreatePipe(&rerr, &werr, NULL, 0);
	SetHandleInformation(rin, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
	SetHandleInformation(wout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
	SetHandleInformation(werr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
	STARTUPINFOA si;
	PROCESS_INFORMATION pi;
	memset(&si, 0, sizeof(STARTUPINFOA));
	si.cb = sizeof(STARTUPINFOA);
	si.dwFlags = STARTF_USESTDHANDLES;
	si.hStdInput = rin;
	si.hStdOutput = wout;
	si.hStdError = werr;
	if (!CreateProcessA(pname, args, NULL, NULL, true, NORMAL_PRIORITY_CLASS, envp, NULL, &si, &pi)) return false;
	p = pi.dwProcessId;
	Sleep(200);
	return true;
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: