1 /**
2 * Copyright (C) ARM Limited 2011-2012. 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 "Sender.h"
18 #include "Logging.h"
19 #include "OlyUtility.h"
20 #include "SessionData.h"
21 #include "CapturedXML.h"
22 #include "StreamlineSetup.h"
23 #include "ConfigurationXML.h"
25 extern void handleException();
27 static const char* TAG_SESSION = "session";
28 static const char* TAG_REQUEST = "request";
29 static const char* TAG_CONFIGURATIONS = "configurations";
31 static const char* ATTR_PROTOCOL = "protocol";
32 static const char* ATTR_EVENTS = "events";
33 static const char* ATTR_CONFIGURATION = "configuration";
34 static const char* ATTR_COUNTERS = "counters";
35 static const char* ATTR_SESSION = "session";
36 static const char* ATTR_CAPTURED = "captured";
37 static const char* ATTR_DEFAULTS = "defaults";
39 StreamlineSetup::StreamlineSetup(OlySocket* s) {
40 bool ready = false;
41 char* data = NULL;
42 int type;
44 mSocket = s;
45 mSessionXML = NULL;
47 // Receive commands from Streamline (master)
48 while (!ready) {
49 // receive command over socket
50 gSessionData->mWaitingOnCommand = true;
51 data = readCommand(&type);
53 // parse and handle data
54 switch (type) {
55 case COMMAND_REQUEST_XML:
56 handleRequest(data);
57 break;
58 case COMMAND_DELIVER_XML:
59 handleDeliver(data);
60 break;
61 case COMMAND_APC_START:
62 logg->logMessage("Received apc start request");
63 ready = true;
64 break;
65 case COMMAND_APC_STOP:
66 logg->logMessage("Received apc stop request before apc start request");
67 exit(0);
68 break;
69 case COMMAND_DISCONNECT:
70 logg->logMessage("Received disconnect command");
71 exit(0);
72 break;
73 case COMMAND_PING:
74 logg->logMessage("Received ping command");
75 sendData(NULL, 0, RESPONSE_ACK);
76 break;
77 default:
78 logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type);
79 handleException();
80 }
82 free(data);
83 }
84 }
86 StreamlineSetup::~StreamlineSetup() {
87 if (mSessionXML) {
88 free(mSessionXML);
89 }
90 }
92 char* StreamlineSetup::readCommand(int* command) {
93 char type;
94 char* data;
95 int response, length;
97 // receive type
98 response = mSocket->receiveNBytes(&type, sizeof(type));
100 // After receiving a single byte, we are no longer waiting on a command
101 gSessionData->mWaitingOnCommand = false;
103 if (response < 0) {
104 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
105 handleException();
106 }
108 // receive length
109 response = mSocket->receiveNBytes((char*)&length, sizeof(length));
110 if (response < 0) {
111 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
112 handleException();
113 }
115 // add artificial limit
116 if ((length < 0) || length > 1024 * 1024) {
117 logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length);
118 handleException();
119 }
121 // allocate memory to contain the xml file, size of zero returns a zero size object
122 data = (char*)calloc(length + 1, 1);
123 if (data == NULL) {
124 logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml");
125 handleException();
126 }
128 // receive data
129 response = mSocket->receiveNBytes(data, length);
130 if (response < 0) {
131 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
132 handleException();
133 }
135 // null terminate the data for string parsing
136 if (length > 0) {
137 data[length] = 0;
138 }
140 *command = type;
141 return data;
142 }
144 void StreamlineSetup::handleRequest(char* xml) {
145 mxml_node_t *tree, *node;
147 tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
148 if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_PROTOCOL, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_PROTOCOL), false)) {
149 sendProtocol();
150 logg->logMessage("Sent protocol xml response");
151 } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_EVENTS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_EVENTS), false)) {
152 sendEvents();
153 logg->logMessage("Sent events xml response");
154 } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_CONFIGURATION, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_CONFIGURATION), false)) {
155 sendConfiguration();
156 logg->logMessage("Sent configuration xml response");
157 } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_COUNTERS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_COUNTERS), false)) {
158 sendCounters();
159 logg->logMessage("Sent counters xml response");
160 } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_SESSION, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_SESSION), false)) {
161 sendData(mSessionXML, strlen(mSessionXML), RESPONSE_XML);
162 logg->logMessage("Sent session xml response");
163 } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_CAPTURED, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_CAPTURED), false)) {
164 CapturedXML capturedXML;
165 char* capturedText = capturedXML.getXML();
166 sendData(capturedText, strlen(capturedText), RESPONSE_XML);
167 free(capturedText);
168 logg->logMessage("Sent captured xml response");
169 } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_DEFAULTS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_DEFAULTS), false)) {
170 sendDefaults();
171 logg->logMessage("Sent default configuration xml response");
172 } else {
173 char error[] = "Unknown request";
174 sendData(error, strlen(error), RESPONSE_NAK);
175 logg->logMessage("Received unknown request:\n%s", xml);
176 }
178 mxmlDelete(tree);
179 }
181 void StreamlineSetup::handleDeliver(char* xml) {
182 mxml_node_t *tree;
184 // Determine xml type
185 tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
186 if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) {
187 // Session XML
188 gSessionData->parseSessionXML(xml);
190 // Save xml
191 mSessionXML = strdup(xml);
192 if (mSessionXML == NULL) {
193 logg->logError(__FILE__, __LINE__, "malloc failed for size %d", strlen(xml) + 1);
194 handleException();
195 }
196 sendData(NULL, 0, RESPONSE_ACK);
197 logg->logMessage("Received session xml");
198 } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) {
199 // Configuration XML
200 writeConfiguration(xml);
201 sendData(NULL, 0, RESPONSE_ACK);
202 logg->logMessage("Received configuration xml");
203 } else {
204 // Unknown XML
205 logg->logMessage("Received unknown XML delivery type");
206 sendData(NULL, 0, RESPONSE_NAK);
207 }
209 mxmlDelete(tree);
210 }
212 void StreamlineSetup::sendData(const char* data, int length, int type) {
213 mSocket->send((char*)&type, 1);
214 mSocket->send((char*)&length, sizeof(length));
215 mSocket->send((char*)data, length);
216 }
218 void StreamlineSetup::sendProtocol() {
219 mxml_node_t *xml;
220 mxml_node_t *protocol;
222 xml = mxmlNewXML("1.0");
223 protocol = mxmlNewElement(xml, "protocol");
224 mxmlElementSetAttrf(protocol, "version", "%d", PROTOCOL_VERSION);
226 char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
227 sendString(string, RESPONSE_XML);
229 free(string);
230 mxmlDelete(xml);
231 }
233 void StreamlineSetup::sendEvents() {
234 #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len
235 char* path = (char*)malloc(PATH_MAX);;
236 char* buffer;
237 unsigned int size = 0;
239 if (gSessionData->mEventsXMLPath) {
240 strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX);
241 } else {
242 util->getApplicationFullPath(path, PATH_MAX);
243 strncat(path, "events.xml", PATH_MAX - strlen(path) - 1);
244 }
245 buffer = util->readFromDisk(path, &size);
246 if (buffer == NULL) {
247 logg->logMessage("Unable to locate events.xml, using default");
248 buffer = (char*)events_xml;
249 size = events_xml_len;
250 }
252 sendData(buffer, size, RESPONSE_XML);
253 if (buffer != (char*)events_xml) {
254 free(buffer);
255 }
256 free(path);
257 }
259 void StreamlineSetup::sendConfiguration() {
260 ConfigurationXML xml;
262 const char* string = xml.getConfigurationXML();
263 sendData(string, strlen(string), RESPONSE_XML);
264 }
266 void StreamlineSetup::sendDefaults() {
267 #include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len
268 // Send the config built into the binary
269 char* xml = (char*)configuration_xml;
270 unsigned int size = configuration_xml_len;
272 // Artificial size restriction
273 if (size > 1024*1024) {
274 logg->logError(__FILE__, __LINE__, "Corrupt default configuration file");
275 handleException();
276 }
278 sendData(xml, size, RESPONSE_XML);
279 }
281 #include <dirent.h>
282 void StreamlineSetup::sendCounters() {
283 struct dirent *ent;
284 mxml_node_t *xml;
285 mxml_node_t *counters;
286 mxml_node_t *counter;
288 // counters.xml is simply a file listing of /dev/gator/events
289 DIR* dir = opendir("/dev/gator/events");
290 if (dir == NULL) {
291 logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events");
292 handleException();
293 }
295 xml = mxmlNewXML("1.0");
296 counters = mxmlNewElement(xml, "counters");
297 while ((ent = readdir(dir)) != NULL) {
298 // skip hidden files, current dir, and parent dir
299 if (ent->d_name[0] == '.')
300 continue;
301 counter = mxmlNewElement(counters, "counter");
302 mxmlElementSetAttr(counter, "name", ent->d_name);
303 }
304 closedir (dir);
306 char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
307 sendString(string, RESPONSE_XML);
309 free(string);
310 mxmlDelete(xml);
311 }
313 void StreamlineSetup::writeConfiguration(char* xml) {
314 char* path = (char*)malloc(PATH_MAX);
316 if (gSessionData->mConfigurationXMLPath) {
317 strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX);
318 } else {
319 util->getApplicationFullPath(path, PATH_MAX);
320 strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1);
321 }
323 if (util->writeToDisk(path, xml) < 0) {
324 logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path);
325 handleException();
326 }
328 // Re-populate gSessionData with the configuration, as it has now changed
329 new ConfigurationXML();
330 free(path);
331 }