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 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 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 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 111 AppRunner::HasQuitted() 112 { 113 port_info info; 114 return (get_port_info(fRemotePort, &info) != B_OK); 115 } 116 117 // WaitFor 118 void 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 129 AppRunner::Team() 130 { 131 return fTeam; 132 } 133 134 // AppLooperPort 135 port_id 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 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 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 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 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 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 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 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 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 259 AppRunner::_UnlockTeamPort() 260 { 261 fTeamPortLock.Unlock(); 262 } 263 264 // _ReadPortID 265 port_id 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 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 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