vfs: check userland buffers before reading them.
[haiku.git] / src / tests / kits / app / common / AppRunner.cpp
blobf3681d45bb40bdcfed8dc3ce5a3ee02695ffffee
1 // AppRunner.cpp
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <unistd.h>
7 #include <Autolock.h>
8 #include <Entry.h>
9 #include <Messenger.h>
10 #include <String.h>
12 #include <TestShell.h>
13 #include <TestUtils.h>
14 #include <cppunit/TestAssert.h>
16 #include "AppRunner.h"
18 static const char *kAppRunnerTeamPort = "app runner team port";
20 // constructor
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)
33 // destructor
34 AppRunner::~AppRunner()
36 if (fRequestQuitOnDestruction)
37 WaitFor(true);
38 if (fReader >= 0) {
39 int32 result;
40 wait_for_thread(fReader, &result);
44 // Run
45 status_t
46 AppRunner::Run(const char *command, const char *args, bool findCommand)
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();
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;
62 // lock the team port
63 bool teamPortLocked = false;
64 if (error == B_OK) {
65 teamPortLocked = _LockTeamPort();
66 if (!teamPortLocked)
67 error = B_ERROR;
69 // run the command
70 if (error == B_OK) {
71 cmdLine += " &";
72 if (system(cmdLine.String()) != 0)
73 error = errno;
75 // read the port ID
76 if (error == B_OK) {
77 fRemotePort = _ReadPortID(fMessenger);
78 if (fRemotePort < 0)
79 error = fRemotePort;
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;
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;
99 // cleanup on error
100 if (error != B_OK) {
101 if (fReader >= 0) {
102 kill_thread(fReader);
103 fReader = -1;
106 return error;
109 // HasQuitted
110 bool
111 AppRunner::HasQuitted()
113 port_info info;
114 return (get_port_info(fRemotePort, &info) != B_OK);
117 // WaitFor
118 void
119 AppRunner::WaitFor(bool requestQuit)
121 if (!HasQuitted() && requestQuit)
122 RequestQuit();
123 while (!HasQuitted())
124 snooze(10000);
127 // Team
128 team_id
129 AppRunner::Team()
131 return fTeam;
134 // AppLooperPort
135 port_id
136 AppRunner::AppLooperPort()
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;
149 return ((messenger_hack*)&fMessenger)->fPort;
152 // GetRef
153 status_t
154 AppRunner::GetRef(entry_ref *ref)
156 status_t error = (ref ? B_OK : B_ERROR);
157 if (error == B_OK)
158 *ref = fRef;
159 return error;
162 // RequestQuit
163 status_t
164 AppRunner::RequestQuit()
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;
175 // GetOutput
176 status_t
177 AppRunner::GetOutput(BString *buffer)
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 = "";
189 return error;
192 // ReadOutput
193 ssize_t
194 AppRunner::ReadOutput(void *buffer, size_t size)
196 BAutolock locker(fOutputLock);
197 return fOutput.Read(buffer, size);
200 // ReadOutputAt
201 ssize_t
202 AppRunner::ReadOutputAt(off_t position, void *buffer, size_t size)
204 BAutolock locker(fOutputLock);
205 return fOutput.ReadAt(position, buffer, size);
208 // _ReaderEntry
209 int32
210 AppRunner::_ReaderEntry(void *data)
212 int32 result = 0;
213 if (AppRunner *me = (AppRunner*)data)
214 result = me->_ReaderLoop();
215 return result;
218 // _ReaderLoop
219 int32
220 AppRunner::_ReaderLoop()
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;
237 fRemotePort = -1;
238 return 0;
241 // _LockTeamPort
242 bool
243 AppRunner::_LockTeamPort()
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;
254 return result;
257 // _UnlockTeamPort
258 void
259 AppRunner::_UnlockTeamPort()
261 fTeamPortLock.Unlock();
264 // _ReadPortID
265 port_id
266 AppRunner::_ReadPortID(BMessenger &messenger)
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;
276 // fTeamPort
277 port_id AppRunner::fTeamPort = -1;
279 // fTeamPortLock
280 BLocker AppRunner::fTeamPortLock;
283 // find_test_app
284 status_t
285 find_test_app(const char *testApp, BString *path)
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
297 return error;
300 // find_test_app
301 status_t
302 find_test_app(const char *testApp, entry_ref *ref)
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;