1/*------------------------------------------------------------------------- 2 * drawElements Quality Program Execution Server 3 * --------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief ExecServer Client. 22 *//*--------------------------------------------------------------------*/ 23 24#include "xsDefs.hpp" 25#include "xsProtocol.hpp" 26#include "deSocket.hpp" 27#include "deUniquePtr.hpp" 28 29#include "deString.h" 30 31#include <memory> 32#include <sstream> 33#include <fstream> 34#include <cstdio> 35#include <cstdlib> 36 37using std::string; 38using std::vector; 39 40namespace xs 41{ 42 43typedef de::UniquePtr<Message> ScopedMsgPtr; 44 45class SocketError : public Error 46{ 47public: 48 SocketError (deSocketResult result, const char* message, const char* file, int line) 49 : Error (message, deGetSocketResultName(result), file, line) 50 , m_result (result) 51 { 52 } 53 54 deSocketResult getResult (void) const 55 { 56 return m_result; 57 } 58 59private: 60 deSocketResult m_result; 61}; 62 63// Helpers. 64void sendMessage (de::Socket& socket, const Message& message) 65{ 66 // Format message. 67 vector<deUint8> buf; 68 message.write(buf); 69 70 // Write to socket. 71 size_t pos = 0; 72 while (pos < buf.size()) 73 { 74 size_t numLeft = buf.size() - pos; 75 size_t numSent = 0; 76 deSocketResult result = socket.send(&buf[pos], numLeft, &numSent); 77 78 if (result != DE_SOCKETRESULT_SUCCESS) 79 throw SocketError(result, "send() failed", __FILE__, __LINE__); 80 81 pos += numSent; 82 } 83} 84 85void readBytes (de::Socket& socket, vector<deUint8>& dst, size_t numBytes) 86{ 87 size_t numRead = 0; 88 dst.resize(numBytes); 89 while (numRead < numBytes) 90 { 91 size_t numLeft = numBytes - numRead; 92 size_t curNumRead = 0; 93 deSocketResult result = socket.receive(&dst[numRead], numLeft, &curNumRead); 94 95 if (result != DE_SOCKETRESULT_SUCCESS) 96 throw SocketError(result, "receive() failed", __FILE__, __LINE__); 97 98 numRead += curNumRead; 99 } 100} 101 102Message* readMessage (de::Socket& socket) 103{ 104 // Header. 105 vector<deUint8> header; 106 readBytes(socket, header, MESSAGE_HEADER_SIZE); 107 108 MessageType type; 109 size_t messageSize; 110 Message::parseHeader(&header[0], (int)header.size(), type, messageSize); 111 112 // Simple messages without any data. 113 switch (type) 114 { 115 case MESSAGETYPE_KEEPALIVE: return new KeepAliveMessage(); 116 case MESSAGETYPE_PROCESS_STARTED: return new ProcessStartedMessage(); 117 default: 118 break; // Read message with data. 119 } 120 121 vector<deUint8> messageBuf; 122 readBytes(socket, messageBuf, messageSize-MESSAGE_HEADER_SIZE); 123 124 switch (type) 125 { 126 case MESSAGETYPE_HELLO: return new HelloMessage(&messageBuf[0], (int)messageBuf.size()); 127 case MESSAGETYPE_TEST: return new TestMessage(&messageBuf[0], (int)messageBuf.size()); 128 case MESSAGETYPE_PROCESS_LOG_DATA: return new ProcessLogDataMessage(&messageBuf[0], (int)messageBuf.size()); 129 case MESSAGETYPE_INFO: return new InfoMessage(&messageBuf[0], (int)messageBuf.size()); 130 case MESSAGETYPE_PROCESS_LAUNCH_FAILED: return new ProcessLaunchFailedMessage(&messageBuf[0], (int)messageBuf.size()); 131 case MESSAGETYPE_PROCESS_FINISHED: return new ProcessFinishedMessage(&messageBuf[0], (int)messageBuf.size()); 132 default: 133 XS_FAIL("Unknown message"); 134 } 135} 136 137class CommandLine 138{ 139public: 140 de::SocketAddress address; 141 std::string program; 142 std::string params; 143 std::string workingDir; 144 std::string caseList; 145 std::string dstFileName; 146}; 147 148class Client 149{ 150public: 151 Client (const CommandLine& cmdLine); 152 ~Client (void); 153 154 void run (void); 155 156private: 157 const CommandLine& m_cmdLine; 158 de::Socket m_socket; 159}; 160 161Client::Client (const CommandLine& cmdLine) 162 : m_cmdLine(cmdLine) 163{ 164} 165 166Client::~Client (void) 167{ 168} 169 170void Client::run (void) 171{ 172 // Connect to server. 173 m_socket.connect(m_cmdLine.address); 174 175 printf("Connected to %s:%d!\n", m_cmdLine.address.getHost(), m_cmdLine.address.getPort()); 176 177 // Open result file. 178 std::fstream out(m_cmdLine.dstFileName.c_str(), std::fstream::out|std::fstream::binary); 179 180 printf(" writing to %s\n", m_cmdLine.dstFileName.c_str()); 181 182 // Send execution request. 183 { 184 ExecuteBinaryMessage msg; 185 186 msg.name = m_cmdLine.program; 187 msg.params = m_cmdLine.params; 188 msg.workDir = m_cmdLine.workingDir; 189 msg.caseList = m_cmdLine.caseList; 190 191 sendMessage(m_socket, msg); 192 printf(" execution request sent.\n"); 193 } 194 195 // Run client loop. 196 bool isRunning = true; 197 while (isRunning) 198 { 199 ScopedMsgPtr msg(readMessage(m_socket)); 200 201 switch (msg->type) 202 { 203 case MESSAGETYPE_HELLO: 204 printf(" HelloMessage\n"); 205 break; 206 207 case MESSAGETYPE_KEEPALIVE: 208 { 209 printf(" KeepAliveMessage\n"); 210 211 // Reply with keepalive. 212 sendMessage(m_socket, KeepAliveMessage()); 213 break; 214 } 215 216 case MESSAGETYPE_INFO: 217 printf(" InfoMessage: '%s'\n", static_cast<InfoMessage*>(msg.get())->info.c_str()); 218 break; 219 220 case MESSAGETYPE_PROCESS_STARTED: 221 printf(" ProcessStartedMessage\n"); 222 break; 223 224 case MESSAGETYPE_PROCESS_FINISHED: 225 printf(" ProcessFinished: exit code = %d\n", static_cast<ProcessFinishedMessage*>(msg.get())->exitCode); 226 isRunning = false; 227 break; 228 229 case MESSAGETYPE_PROCESS_LAUNCH_FAILED: 230 printf(" ProcessLaunchFailed: '%s'\n", static_cast<ProcessLaunchFailedMessage*>(msg.get())->reason.c_str()); 231 isRunning = false; 232 break; 233 234 case MESSAGETYPE_PROCESS_LOG_DATA: 235 { 236 ProcessLogDataMessage* logDataMsg = static_cast<ProcessLogDataMessage*>(msg.get()); 237 printf(" ProcessLogDataMessage: %d bytes\n", (int)logDataMsg->logData.length()); 238 out << logDataMsg->logData; 239 break; 240 } 241 242 default: 243 XS_FAIL("Unknown message"); 244 break; 245 } 246 } 247 248 // Close output file. 249 out.close(); 250 251 // Close connection. 252 m_socket.shutdown(); 253 m_socket.close(); 254 255 printf("Done!\n"); 256} 257 258string parseString (const char* str) 259{ 260 if (str[0] == '\'' || str[0] == '"') 261 { 262 const char* p = str; 263 char endChar = *p++; 264 std::ostringstream o; 265 266 while (*p != endChar && *p) 267 { 268 if (*p == '\\') 269 { 270 switch (p[1]) 271 { 272 case 0: DE_ASSERT(DE_FALSE); break; 273 case 'n': o << '\n'; break; 274 case 't': o << '\t'; break; 275 default: o << p[1]; break; 276 } 277 278 p += 2; 279 } 280 else 281 o << *p++; 282 } 283 284 return o.str(); 285 } 286 else 287 return string(str); 288} 289 290void printHelp (const char* binName) 291{ 292 printf("%s:\n", binName); 293 printf(" --host=[host] Connect to host [host]\n"); 294 printf(" --port=[name] Use port [port]\n"); 295 printf(" --program=[program] Test program\n"); 296 printf(" --params=[params] Test program params\n"); 297 printf(" --workdir=[dir] Working directory\n"); 298 printf(" --caselist=[caselist] Test case list\n"); 299 printf(" --out=filename Test result file\n"); 300} 301 302int runClient (int argc, const char* const* argv) 303{ 304 CommandLine cmdLine; 305 306 // Defaults. 307 cmdLine.address.setHost("127.0.0.1"); 308 cmdLine.address.setPort(50016); 309 cmdLine.dstFileName = "TestResults.qpa"; 310 311 // Parse command line. 312 for (int argNdx = 1; argNdx < argc; argNdx++) 313 { 314 const char* arg = argv[argNdx]; 315 316 if (deStringBeginsWith(arg, "--port=")) 317 cmdLine.address.setPort(atoi(arg+7)); 318 else if (deStringBeginsWith(arg, "--host=")) 319 cmdLine.address.setHost(parseString(arg+7).c_str()); 320 else if (deStringBeginsWith(arg, "--program=")) 321 cmdLine.program = parseString(arg+10); 322 else if (deStringBeginsWith(arg, "--params=")) 323 cmdLine.params = parseString(arg+9); 324 else if (deStringBeginsWith(arg, "--workdir=")) 325 cmdLine.workingDir = parseString(arg+10); 326 else if (deStringBeginsWith(arg, "--caselist=")) 327 cmdLine.caseList = parseString(arg+11); 328 else if (deStringBeginsWith(arg, "--out=")) 329 cmdLine.dstFileName = parseString(arg+6); 330 else 331 { 332 printHelp(argv[0]); 333 return -1; 334 } 335 } 336 337 // Run client. 338 try 339 { 340 Client client(cmdLine); 341 client.run(); 342 } 343 catch (const std::exception& e) 344 { 345 printf("%s\n", e.what()); 346 return -1; 347 } 348 349 return 0; 350} 351 352} // xs 353 354int main (int argc, const char* const* argv) 355{ 356 return xs::runClient(argc, argv); 357} 358