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