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