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 
36 using std::string;
37 using std::vector;
38 
39 namespace xs
40 {
41 
42 typedef std::auto_ptr<Message> ScopedMsgPtr;
43 
44 class SocketError : public Error
45 {
46 public:
SocketError(deSocketResult result,const char * message,const char * file,int line)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 
getResult(void) const53 	deSocketResult getResult (void) const
54 	{
55 		return m_result;
56 	}
57 
58 private:
59 	deSocketResult m_result;
60 };
61 
62 // Helpers.
sendMessage(de::Socket & socket,const Message & message)63 void 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 
readBytes(de::Socket & socket,vector<deUint8> & dst,int numBytes)84 void 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 
readMessage(de::Socket & socket)101 Message* 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 
136 class CommandLine
137 {
138 public:
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 
147 class Client
148 {
149 public:
150 						Client		(const CommandLine& cmdLine);
151 						~Client		(void);
152 
153 	void				run			(void);
154 
155 private:
156 	const CommandLine&	m_cmdLine;
157 	de::Socket			m_socket;
158 };
159 
Client(const CommandLine & cmdLine)160 Client::Client (const CommandLine& cmdLine)
161 	: m_cmdLine(cmdLine)
162 {
163 }
164 
~Client(void)165 Client::~Client (void)
166 {
167 }
168 
run(void)169 void 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 
parseString(const char * str)257 string 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 
printHelp(const char * binName)289 void 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 
runClient(int argc,const char * const * argv)301 int 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 
main(int argc,const char * const * argv)353 int main (int argc, const char* const* argv)
354 {
355 	return xs::runClient(argc, argv);
356 }
357