1// PipedAppRunner.cpp
2
3#include <errno.h>
4#include <unistd.h>
5
6#include <Autolock.h>
7#include <String.h>
8
9#include <TestShell.h>
10#include <TestUtils.h>
11#include <cppunit/TestAssert.h>
12
13#include "PipedAppRunner.h"
14
15// constructor
16PipedAppRunner::PipedAppRunner()
17			  : fOutputLock(),
18				fPipe(NULL),
19				fOutput(),
20				fReader(-1)
21{
22}
23
24// destructor
25PipedAppRunner::~PipedAppRunner()
26{
27	if (fReader >= 0) {
28		_ClosePipe();
29		int32 result;
30		wait_for_thread(fReader, &result);
31	}
32}
33
34// Run
35status_t
36PipedAppRunner::Run(const char *command, const char *args, bool findCommand)
37{
38	status_t error = (HasQuitted() ? B_OK : B_ERROR);
39	// get the app path
40	BString appPath;
41	if (findCommand) {
42		appPath = BTestShell::GlobalTestDir();
43		appPath.CharacterEscape(" \t\n!\"'`$&()?*+{}[]<>|", '\\');
44		appPath += "/";
45		appPath += command;
46		#ifdef TEST_R5
47			appPath += "_r5";
48		#endif
49		command = appPath.String();
50	}
51	// add args, i.e. compose the command line
52	BString cmdLine(command);
53	if (args) {
54		cmdLine += " ";
55		cmdLine += args;
56	}
57	// run the command
58	if (error == B_OK) {
59		fPipe = popen(cmdLine.String(), "r");
60		if (!fPipe)
61			error = errno;
62	}
63	// spawn the reader thread
64	if (error == B_OK) {
65		fReader = spawn_thread(&_ReaderEntry, "PipedAppRunner reader",
66							   B_NORMAL_PRIORITY, (void*)this);
67		if (fReader >= 0)
68			error = resume_thread(fReader);
69		else
70			error = fReader;
71	}
72	// cleanup on error
73	if (error != B_OK) {
74		if (fReader >= 0) {
75			kill_thread(fReader);
76			fReader = -1;
77		}
78		if (fPipe) {
79			pclose(fPipe);
80			fPipe = NULL;
81		}
82	}
83	return error;
84}
85
86// HasQuitted
87bool
88PipedAppRunner::HasQuitted()
89{
90	BAutolock locker(fOutputLock);
91	return !fPipe;
92}
93
94// WaitFor
95void
96PipedAppRunner::WaitFor()
97{
98	while (!HasQuitted())
99		snooze(10000);
100}
101
102// GetOutput
103status_t
104PipedAppRunner::GetOutput(BString *buffer)
105{
106	status_t error = (buffer ? B_OK : B_BAD_VALUE);
107	if (error == B_OK) {
108		BAutolock locker(fOutputLock);
109		size_t size = fOutput.BufferLength();
110		const void *output = fOutput.Buffer();
111		if (size > 0)
112			buffer->SetTo((const char*)output, size);
113		else
114			*buffer = "";
115	}
116	return error;
117}
118
119// ReadOutput
120ssize_t
121PipedAppRunner::ReadOutput(void *buffer, size_t size)
122{
123	BAutolock locker(fOutputLock);
124	return fOutput.Read(buffer, size);
125}
126
127// ReadOutputAt
128ssize_t
129PipedAppRunner::ReadOutputAt(off_t position, void *buffer, size_t size)
130{
131	BAutolock locker(fOutputLock);
132	return fOutput.ReadAt(position, buffer, size);
133}
134
135// _ReaderEntry
136int32
137PipedAppRunner::_ReaderEntry(void *data)
138{
139	int32 result = 0;
140	if (PipedAppRunner *me = (PipedAppRunner*)data)
141		result = me->_ReaderLoop();
142	return result;
143}
144
145// _ReaderLoop
146int32
147PipedAppRunner::_ReaderLoop()
148{
149	char buffer[10240];
150	fOutputLock.Lock();
151	FILE *pipe = fPipe;
152	fOutputLock.Unlock();
153	while (!feof(pipe)) {
154		size_t bytes = fread(buffer, 1, sizeof(buffer), pipe);
155		if (bytes > 0) {
156			BAutolock locker(fOutputLock);
157			off_t oldPosition = fOutput.Seek(0, SEEK_END);
158			fOutput.Write(buffer, bytes);
159			fOutput.Seek(oldPosition, SEEK_SET);
160		}
161	}
162	_ClosePipe();
163	return 0;
164}
165
166// _ClosePipe
167void
168PipedAppRunner::_ClosePipe()
169{
170	if (fOutputLock.Lock()) {
171		if (fPipe) {
172			pclose(fPipe);
173			fPipe = NULL;
174		}
175		fOutputLock.Unlock();
176	}
177}
178
179