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 18static const char *kAppRunnerTeamPort = "app runner team port"; 19 20// constructor 21AppRunner::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 34AppRunner::~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 45status_t 46AppRunner::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 110bool 111AppRunner::HasQuitted() 112{ 113 port_info info; 114 return (get_port_info(fRemotePort, &info) != B_OK); 115} 116 117// WaitFor 118void 119AppRunner::WaitFor(bool requestQuit) 120{ 121 if (!HasQuitted() && requestQuit) 122 RequestQuit(); 123 while (!HasQuitted()) 124 snooze(10000); 125} 126 127// Team 128team_id 129AppRunner::Team() 130{ 131 return fTeam; 132} 133 134// AppLooperPort 135port_id 136AppRunner::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 153status_t 154AppRunner::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 163status_t 164AppRunner::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 176status_t 177AppRunner::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 193ssize_t 194AppRunner::ReadOutput(void *buffer, size_t size) 195{ 196 BAutolock locker(fOutputLock); 197 return fOutput.Read(buffer, size); 198} 199 200// ReadOutputAt 201ssize_t 202AppRunner::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 209int32 210AppRunner::_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 219int32 220AppRunner::_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 242bool 243AppRunner::_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 258void 259AppRunner::_UnlockTeamPort() 260{ 261 fTeamPortLock.Unlock(); 262} 263 264// _ReadPortID 265port_id 266AppRunner::_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 277port_id AppRunner::fTeamPort = -1; 278 279// fTeamPortLock 280BLocker AppRunner::fTeamPortLock; 281 282 283// find_test_app 284status_t 285find_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 301status_t 302find_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