1 /**
2 * Copyright (C) ARM Limited 2011. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
9 #include <string.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <dirent.h>
13 #include <sys/types.h>
14 #include <arpa/inet.h>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include "XMLOut.h"
18 #include "Sender.h"
19 #include "Logging.h"
20 #include "XMLReader.h"
21 #include "RequestXML.h"
22 #include "OlyUtility.h"
23 #include "SessionData.h"
24 #include "CapturedXML.h"
25 #include "StreamlineSetup.h"
26 #include "ConfigurationXML.h"
28 extern void handleException();
30 static const char* TAG_SESSION = "session";
31 static const char* TAG_CONFIGURATIONS = "configurations";
33 StreamlineSetup::StreamlineSetup(OlySocket* s) {
34 bool ready = false;
35 char *data = NULL;
36 int type;
38 socket = s;
39 mSessionXML = NULL;
41 // Receive commands from Streamline (master)
42 while (!ready) {
43 // receive command over socket
44 gSessionData.mWaitingOnCommand = true;
45 data = readCommand(&type);
47 // parse and handle data
48 switch (type) {
49 case COMMAND_REQUEST_XML:
50 handleRequest(data);
51 break;
52 case COMMAND_DELIVER_XML:
53 handleDeliver(data);
54 break;
55 case COMMAND_APC_START:
56 logg->logMessage("Received apc start request");
57 ready = true;
58 break;
59 case COMMAND_APC_STOP:
60 // Clear error log so no text appears on console and exit
61 logg->logMessage("Received apc stop request before apc start request");
62 logg->logError(__FILE__, __LINE__, "");
63 handleException();
64 break;
65 case COMMAND_DISCONNECT:
66 // Clear error log so no text appears on console and exit
67 logg->logMessage("Received disconnect command");
68 logg->logError(__FILE__, __LINE__, "");
69 handleException();
70 break;
71 case COMMAND_PING:
72 logg->logMessage("Received ping command");
73 sendData(NULL, 0, RESPONSE_ACK);
74 break;
75 default:
76 logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type);
77 handleException();
78 }
80 delete(data);
81 }
82 }
84 StreamlineSetup::~StreamlineSetup() {
85 if (mSessionXML)
86 free(mSessionXML);
87 }
89 char* StreamlineSetup::readCommand(int* command) {
90 char type;
91 char* data;
92 int response, length;
94 // receive type
95 response = socket->receiveNBytes(&type, sizeof(type));
97 // After receiving a single byte, we are no longer waiting on a command
98 gSessionData.mWaitingOnCommand = false;
100 if (response < 0) {
101 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
102 handleException();
103 }
105 // receive length
106 response = socket->receiveNBytes((char*)&length, sizeof(length));
107 if (response < 0) {
108 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
109 handleException();
110 }
112 // add artificial limit
113 if ((length < 0) || length > 1024 * 1024) {
114 logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length);
115 handleException();
116 }
118 // allocate memory to contain the xml file, size of zero returns a zero size object
119 data = (char*)calloc(length + 1, 1);
120 if (data == NULL) {
121 logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml");
122 handleException();
123 }
125 // receive data
126 response = socket->receiveNBytes(data, length);
127 if (response < 0) {
128 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
129 handleException();
130 }
132 // null terminate the data for string parsing
133 if (length > 0) {
134 data[length] = 0;
135 }
137 *command = type;
138 return data;
139 }
141 void StreamlineSetup::handleRequest(char* xml) {
142 RequestXML request(xml);
144 if (request.parameters.protocol) {
145 sendProtocol();
146 logg->logMessage("Sent protocol xml response");
147 } else if (request.parameters.events) {
148 sendEvents();
149 logg->logMessage("Sent events xml response");
150 } else if (request.parameters.configuration) {
151 sendConfiguration();
152 logg->logMessage("Sent configuration xml response");
153 } else if (request.parameters.counters) {
154 sendCounters();
155 logg->logMessage("Sent counters xml response");
156 } else if (request.parameters.session) {
157 sendData(mSessionXML, strlen(mSessionXML), RESPONSE_XML);
158 logg->logMessage("Sent session xml response");
159 } else if (request.parameters.captured) {
160 CapturedXML capturedXML;
161 const char* capturedText = capturedXML.getXML();
162 sendData(capturedText, strlen(capturedText), RESPONSE_XML);
163 logg->logMessage("Sent captured xml response");
164 } else if (request.parameters.defaults) {
165 sendDefaults();
166 logg->logMessage("Sent default configuration xml response");
167 } else {
168 char error[] = "Unknown request";
169 sendData(error, strlen(error), RESPONSE_NAK);
170 logg->logMessage("Received unknown request:\n%s", xml);
171 }
172 }
174 typedef enum {UNKNOWN, SESSION_XML, CONFIGURATION_XML} delivery_type_t;
175 void StreamlineSetup::handleDeliver(char* xml) {
176 delivery_type_t type = UNKNOWN;
178 // Determine xml type
179 XMLReader reader(xml);
180 char * tag = reader.nextTag();
181 while(tag != 0) {
182 if (strcmp(tag, TAG_SESSION) == 0) {
183 type = SESSION_XML;
184 break;
185 } else if (strcmp(tag, TAG_CONFIGURATIONS) == 0) {
186 type = CONFIGURATION_XML;
187 break;
188 }
189 tag = reader.nextTag();
190 }
192 switch (type) {
193 case UNKNOWN:
194 logg->logMessage("Received unknown delivery type: %d", type);
195 sendData(NULL, 0, RESPONSE_NAK);
196 break;
197 case SESSION_XML:
198 // Parse the session xml
199 gSessionData.parseSessionXML(xml);
201 // Save xml
202 mSessionXML = strdup(xml);
203 if (mSessionXML == NULL) {
204 logg->logError(__FILE__, __LINE__, "malloc failed for size %d", strlen(xml) + 1);
205 handleException();
206 }
207 sendData(NULL, 0, RESPONSE_ACK);
208 logg->logMessage("Received session xml");
209 break;
210 case CONFIGURATION_XML:
211 writeConfiguration(xml);
212 sendData(NULL, 0, RESPONSE_ACK);
213 logg->logMessage("Received configuration xml");
214 break;
215 }
216 }
218 void StreamlineSetup::sendData(const char* data, int length, int type) {
219 socket->send((char*)&type, 1);
220 socket->send((char*)&length, sizeof(length));
221 socket->send((char*)data, length);
222 }
224 void StreamlineSetup::sendProtocol() {
225 XMLOut out;
226 out.xmlHeader();
228 out.startElement("protocol");
229 out.attributeInt("version", PROTOCOL_VERSION);
230 out.endElement("protocol");
232 sendString(out.getXmlString(), RESPONSE_XML);
233 }
235 void StreamlineSetup::sendEvents() {
236 #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len
237 char* path = (char*)malloc(PATH_MAX);;
238 char* buffer;
239 unsigned int size = 0;
241 util->getApplicationFullPath(path, PATH_MAX);
242 strncat(path, "events.xml", PATH_MAX - strlen(path) - 1);
243 buffer = util->readFromDisk(path, &size);
244 if (buffer == NULL) {
245 logg->logMessage("Unable to locate events.xml, using default");
246 buffer = (char*)events_xml;
247 size = events_xml_len;
248 }
250 sendData(buffer, size, RESPONSE_XML);
251 if (buffer != (char*)events_xml) {
252 free(buffer);
253 }
254 free(path);
255 }
257 void StreamlineSetup::sendConfiguration() {
258 ConfigurationXML xml;
260 const char* string = xml.getConfigurationXML();
261 sendData(string, strlen(string), RESPONSE_XML);
262 }
264 void StreamlineSetup::sendDefaults() {
265 #include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len
266 // Send the config built into the binary
267 char* xml = (char*)configuration_xml;
268 unsigned int size = configuration_xml_len;
270 // Artificial size restriction
271 if (size > 1024*1024) {
272 logg->logError(__FILE__, __LINE__, "Corrupt default configuration file");
273 handleException();
274 }
276 sendData(xml, size, RESPONSE_XML);
277 }
279 #include <dirent.h>
280 void StreamlineSetup::sendCounters() {
281 XMLOut out;
282 struct dirent *ent;
284 // counters.xml is simply a file listing of /dev/gator/events
285 DIR* dir = opendir("/dev/gator/events");
286 if (dir == NULL) {
287 logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events");
288 handleException();
289 }
291 out.xmlHeader();
292 out.startElement("counters");
293 while ((ent = readdir(dir)) != NULL) {
294 // skip hidden files, current dir, and parent dir
295 if (ent->d_name[0] == '.')
296 continue;
297 out.startElement("counter");
298 out.attributeString("name", ent->d_name);
299 out.endElement("counter");
300 }
301 out.endElement("counters");
302 closedir (dir);
304 sendString(out.getXmlString(), RESPONSE_XML);
305 }
307 void StreamlineSetup::writeConfiguration(char* xml) {
308 char* path = (char*)malloc(PATH_MAX);
310 util->getApplicationFullPath(path, PATH_MAX);
311 strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1);
312 if (util->writeToDisk(path, xml) < 0) {
313 logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path);
314 handleException();
315 }
317 new ConfigurationXML();
318 free(path);
319 }