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