xref: /haiku/src/tests/kits/app/common/PipedAppRunner.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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
16 PipedAppRunner::PipedAppRunner()
17 			  : fOutputLock(),
18 				fPipe(NULL),
19 				fOutput(),
20 				fReader(-1)
21 {
22 }
23 
24 // destructor
25 PipedAppRunner::~PipedAppRunner()
26 {
27 	if (fReader >= 0) {
28 		_ClosePipe();
29 		int32 result;
30 		wait_for_thread(fReader, &result);
31 	}
32 }
33 
34 // Run
35 status_t
36 PipedAppRunner::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
87 bool
88 PipedAppRunner::HasQuitted()
89 {
90 	BAutolock locker(fOutputLock);
91 	return !fPipe;
92 }
93 
94 // WaitFor
95 void
96 PipedAppRunner::WaitFor()
97 {
98 	while (!HasQuitted())
99 		snooze(10000);
100 }
101 
102 // GetOutput
103 status_t
104 PipedAppRunner::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
120 ssize_t
121 PipedAppRunner::ReadOutput(void *buffer, size_t size)
122 {
123 	BAutolock locker(fOutputLock);
124 	return fOutput.Read(buffer, size);
125 }
126 
127 // ReadOutputAt
128 ssize_t
129 PipedAppRunner::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
136 int32
137 PipedAppRunner::_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
146 int32
147 PipedAppRunner::_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
167 void
168 PipedAppRunner::_ClosePipe()
169 {
170 	if (fOutputLock.Lock()) {
171 		if (fPipe) {
172 			pclose(fPipe);
173 			fPipe = NULL;
174 		}
175 		fOutputLock.Unlock();
176 	}
177 }
178 
179