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