1 // AppRunner.cpp
2
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6
7 #include <Autolock.h>
8 #include <Entry.h>
9 #include <Messenger.h>
10 #include <String.h>
11
12 #include <TestShell.h>
13 #include <TestUtils.h>
14 #include <cppunit/TestAssert.h>
15
16 #include "AppRunner.h"
17
18 static const char *kAppRunnerTeamPort = "app runner team port";
19
20 // constructor
AppRunner(bool requestQuitOnDestruction)21 AppRunner::AppRunner(bool requestQuitOnDestruction)
22 : fOutputLock(),
23 fRemotePort(-1),
24 fOutput(),
25 fReader(-1),
26 fTeam(-1),
27 fRef(),
28 fMessenger(),
29 fRequestQuitOnDestruction(requestQuitOnDestruction)
30 {
31 }
32
33 // destructor
~AppRunner()34 AppRunner::~AppRunner()
35 {
36 if (fRequestQuitOnDestruction)
37 WaitFor(true);
38 if (fReader >= 0) {
39 int32 result;
40 wait_for_thread(fReader, &result);
41 }
42 }
43
44 // Run
45 status_t
Run(const char * command,const char * args,bool findCommand)46 AppRunner::Run(const char *command, const char *args, bool findCommand)
47 {
48 status_t error = (HasQuitted() ? B_OK : B_ERROR);
49 // get the app path
50 BString appPath;
51 if (findCommand) {
52 find_test_app(command, &appPath);
53 command = appPath.String();
54 }
55 get_ref_for_path(command, &fRef);
56 // add args, i.e. compose the command line
57 BString cmdLine(command);
58 if (args) {
59 cmdLine += " ";
60 cmdLine += args;
61 }
62 // lock the team port
63 bool teamPortLocked = false;
64 if (error == B_OK) {
65 teamPortLocked = _LockTeamPort();
66 if (!teamPortLocked)
67 error = B_ERROR;
68 }
69 // run the command
70 if (error == B_OK) {
71 cmdLine += " &";
72 if (system(cmdLine.String()) != 0)
73 error = errno;
74 }
75 // read the port ID
76 if (error == B_OK) {
77 fRemotePort = _ReadPortID(fMessenger);
78 if (fRemotePort < 0)
79 error = fRemotePort;
80 }
81 // unlock the team port
82 if (teamPortLocked)
83 _UnlockTeamPort();
84 // get the team ID
85 if (error == B_OK) {
86 port_info info;
87 error = get_port_info(fRemotePort, &info);
88 fTeam = info.team;
89 }
90 // spawn the reader thread
91 if (error == B_OK) {
92 fReader = spawn_thread(&_ReaderEntry, "AppRunner reader",
93 B_NORMAL_PRIORITY, (void*)this);
94 if (fReader >= 0)
95 error = resume_thread(fReader);
96 else
97 error = fReader;
98 }
99 // cleanup on error
100 if (error != B_OK) {
101 if (fReader >= 0) {
102 kill_thread(fReader);
103 fReader = -1;
104 }
105 }
106 return error;
107 }
108
109 // HasQuitted
110 bool
HasQuitted()111 AppRunner::HasQuitted()
112 {
113 port_info info;
114 return (get_port_info(fRemotePort, &info) != B_OK);
115 }
116
117 // WaitFor
118 void
WaitFor(bool requestQuit)119 AppRunner::WaitFor(bool requestQuit)
120 {
121 if (!HasQuitted() && requestQuit)
122 RequestQuit();
123 while (!HasQuitted())
124 snooze(10000);
125 }
126
127 // Team
128 team_id
Team()129 AppRunner::Team()
130 {
131 return fTeam;
132 }
133
134 // AppLooperPort
135 port_id
AppLooperPort()136 AppRunner::AppLooperPort()
137 {
138 struct messenger_hack {
139 port_id fPort;
140 int32 fHandlerToken;
141 team_id fTeam;
142 int32 extra0;
143 int32 extra1;
144 bool fPreferredTarget;
145 bool extra2;
146 bool extra3;
147 bool extra4;
148 };
149 return ((messenger_hack*)&fMessenger)->fPort;
150 }
151
152 // GetRef
153 status_t
GetRef(entry_ref * ref)154 AppRunner::GetRef(entry_ref *ref)
155 {
156 status_t error = (ref ? B_OK : B_ERROR);
157 if (error == B_OK)
158 *ref = fRef;
159 return error;
160 }
161
162 // RequestQuit
163 status_t
RequestQuit()164 AppRunner::RequestQuit()
165 {
166 status_t error = B_OK;
167 if (fTeam >= 0) {
168 BMessenger messenger(fMessenger);
169 error = messenger.SendMessage(B_QUIT_REQUESTED);
170 } else
171 error = fTeam;
172 return error;
173 }
174
175 // GetOutput
176 status_t
GetOutput(BString * buffer)177 AppRunner::GetOutput(BString *buffer)
178 {
179 status_t error = (buffer ? B_OK : B_BAD_VALUE);
180 if (error == B_OK) {
181 BAutolock locker(fOutputLock);
182 size_t size = fOutput.BufferLength();
183 const void *output = fOutput.Buffer();
184 if (size > 0)
185 buffer->SetTo((const char*)output, size);
186 else
187 *buffer = "";
188 }
189 return error;
190 }
191
192 // ReadOutput
193 ssize_t
ReadOutput(void * buffer,size_t size)194 AppRunner::ReadOutput(void *buffer, size_t size)
195 {
196 BAutolock locker(fOutputLock);
197 return fOutput.Read(buffer, size);
198 }
199
200 // ReadOutputAt
201 ssize_t
ReadOutputAt(off_t position,void * buffer,size_t size)202 AppRunner::ReadOutputAt(off_t position, void *buffer, size_t size)
203 {
204 BAutolock locker(fOutputLock);
205 return fOutput.ReadAt(position, buffer, size);
206 }
207
208 // _ReaderEntry
209 int32
_ReaderEntry(void * data)210 AppRunner::_ReaderEntry(void *data)
211 {
212 int32 result = 0;
213 if (AppRunner *me = (AppRunner*)data)
214 result = me->_ReaderLoop();
215 return result;
216 }
217
218 // _ReaderLoop
219 int32
_ReaderLoop()220 AppRunner::_ReaderLoop()
221 {
222 char buffer[10240];
223 port_id port = fRemotePort;
224 status_t error = B_OK;
225 while (error == B_OK) {
226 int32 code;
227 ssize_t bytes = read_port(port, &code, buffer, sizeof(buffer) - 1);
228 if (bytes > 0) {
229 // write the buffer
230 BAutolock locker(fOutputLock);
231 off_t oldPosition = fOutput.Seek(0, SEEK_END);
232 fOutput.Write(buffer, bytes);
233 fOutput.Seek(oldPosition, SEEK_SET);
234 } else if (bytes < 0)
235 error = bytes;
236 }
237 fRemotePort = -1;
238 return 0;
239 }
240
241 // _LockTeamPort
242 bool
_LockTeamPort()243 AppRunner::_LockTeamPort()
244 {
245 bool result = fTeamPortLock.Lock();
246 // lazy port creation
247 if (result && fTeamPort < 0) {
248 fTeamPort = create_port(5, kAppRunnerTeamPort);
249 if (fTeamPort < 0) {
250 fTeamPortLock.Unlock();
251 result = false;
252 }
253 }
254 return result;
255 }
256
257 // _UnlockTeamPort
258 void
_UnlockTeamPort()259 AppRunner::_UnlockTeamPort()
260 {
261 fTeamPortLock.Unlock();
262 }
263
264 // _ReadPortID
265 port_id
_ReadPortID(BMessenger & messenger)266 AppRunner::_ReadPortID(BMessenger &messenger)
267 {
268 port_id port = -1;
269 ssize_t size = read_port(fTeamPort, &port, &messenger, sizeof(BMessenger));
270 if (size < 0)
271 port = size;
272 return port;
273 }
274
275
276 // fTeamPort
277 port_id AppRunner::fTeamPort = -1;
278
279 // fTeamPortLock
280 BLocker AppRunner::fTeamPortLock;
281
282
283 // find_test_app
284 status_t
find_test_app(const char * testApp,BString * path)285 find_test_app(const char *testApp, BString *path)
286 {
287 status_t error = (testApp && path ? B_OK : B_BAD_VALUE);
288 if (error == B_OK) {
289 *path = BTestShell::GlobalTestDir();
290 path->CharacterEscape(" \t\n!\"'`$&()?*+{}[]<>|", '\\');
291 *path += "/";
292 *path += testApp;
293 #ifdef TEST_R5
294 *path += "_r5";
295 #endif
296 }
297 return error;
298 }
299
300 // find_test_app
301 status_t
find_test_app(const char * testApp,entry_ref * ref)302 find_test_app(const char *testApp, entry_ref *ref)
303 {
304 status_t error = (testApp && ref ? B_OK : B_BAD_VALUE);
305 BString path;
306 if (error == B_OK)
307 error = find_test_app(testApp, &path);
308 if (error == B_OK)
309 error = get_ref_for_path(path.String(), ref);
310 return error;
311 }
312
313