e196a7dba2c993151eff4328d06398ab23b07732
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / StreamlineSetup.cpp
1 /**
2  * Copyright (C) ARM Limited 2011-2013. 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  */
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <arpa/inet.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include "Sender.h"
16 #include "Logging.h"
17 #include "OlyUtility.h"
18 #include "SessionData.h"
19 #include "CapturedXML.h"
20 #include "StreamlineSetup.h"
21 #include "ConfigurationXML.h"
22 #include "Driver.h"
23 #include "EventsXML.h"
24
25 static const char* TAG_SESSION = "session";
26 static const char* TAG_REQUEST = "request";
27 static const char* TAG_CONFIGURATIONS = "configurations";
28
29 static const char* ATTR_TYPE           = "type";
30 static const char* VALUE_EVENTS        = "events";
31 static const char* VALUE_CONFIGURATION = "configuration";
32 static const char* VALUE_COUNTERS      = "counters";
33 static const char* VALUE_CAPTURED      = "captured";
34 static const char* VALUE_DEFAULTS      = "defaults";
35
36 StreamlineSetup::StreamlineSetup(OlySocket* s) {
37         bool ready = false;
38         char* data = NULL;
39         int type;
40
41         mSocket = s;
42
43         // Receive commands from Streamline (master)
44         while (!ready) {
45                 // receive command over socket
46                 gSessionData->mWaitingOnCommand = true;
47                 data = readCommand(&type);
48
49                 // parse and handle data
50                 switch (type) {
51                         case COMMAND_REQUEST_XML:
52                                 handleRequest(data);
53                                 break;
54                         case COMMAND_DELIVER_XML:
55                                 handleDeliver(data);
56                                 break;
57                         case COMMAND_APC_START:
58                                 logg->logMessage("Received apc start request");
59                                 ready = true;
60                                 break;
61                         case COMMAND_APC_STOP:
62                                 logg->logMessage("Received apc stop request before apc start request");
63                                 exit(0);
64                                 break;
65                         case COMMAND_DISCONNECT:
66                                 logg->logMessage("Received disconnect command");
67                                 exit(0);
68                                 break;
69                         case COMMAND_PING:
70                                 logg->logMessage("Received ping command");
71                                 sendData(NULL, 0, RESPONSE_ACK);
72                                 break;
73                         default:
74                                 logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type);
75                                 handleException();
76                 }
77
78                 free(data);
79         }
80
81         if (gSessionData->mCounterOverflow > 0) {
82                 logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
83                 handleException();
84         }
85 }
86
87 StreamlineSetup::~StreamlineSetup() {
88 }
89
90 char* StreamlineSetup::readCommand(int* command) {
91         unsigned char header[5];
92         char* data;
93         int response;
94
95         // receive type and length
96         response = mSocket->receiveNBytes((char*)&header, sizeof(header));
97
98         // After receiving a single byte, we are no longer waiting on a command
99         gSessionData->mWaitingOnCommand = false;
100
101         if (response < 0) {
102                 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
103                 handleException();
104         }
105
106         const char type = header[0];
107         const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
108
109         // add artificial limit
110         if ((length < 0) || length > 1024 * 1024) {
111                 logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length);
112                 handleException();
113         }
114
115         // allocate memory to contain the xml file, size of zero returns a zero size object
116         data = (char*)calloc(length + 1, 1);
117         if (data == NULL) {
118                 logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml");
119                 handleException();
120         }
121
122         // receive data
123         response = mSocket->receiveNBytes(data, length);
124         if (response < 0) {
125                 logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect");
126                 handleException();
127         }
128
129         // null terminate the data for string parsing
130         if (length > 0) {
131                 data[length] = 0;
132         }
133
134         *command = type;
135         return data;
136 }
137
138 void StreamlineSetup::handleRequest(char* xml) {
139         mxml_node_t *tree, *node;
140         const char * attr = NULL;
141
142         tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
143         node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_TYPE, NULL, MXML_DESCEND_FIRST);
144         if (node) {
145                 attr = mxmlElementGetAttr(node, ATTR_TYPE);
146         }
147         if (attr && strcmp(attr, VALUE_EVENTS) == 0) {
148                 sendEvents();
149                 logg->logMessage("Sent events xml response");
150         } else if (attr && strcmp(attr, VALUE_CONFIGURATION) == 0) {
151                 sendConfiguration();
152                 logg->logMessage("Sent configuration xml response");
153         } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) {
154                 sendCounters();
155                 logg->logMessage("Sent counters xml response");
156         } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) {
157                 CapturedXML capturedXML;
158                 char* capturedText = capturedXML.getXML(false);
159                 sendData(capturedText, strlen(capturedText), RESPONSE_XML);
160                 free(capturedText);
161                 logg->logMessage("Sent captured xml response");
162         } else if (attr && strcmp(attr, VALUE_DEFAULTS) == 0) {
163                 sendDefaults();
164                 logg->logMessage("Sent default configuration xml response");
165         } else {
166                 char error[] = "Unknown request";
167                 sendData(error, strlen(error), RESPONSE_NAK);
168                 logg->logMessage("Received unknown request:\n%s", xml);
169         }
170
171         mxmlDelete(tree);
172 }
173
174 void StreamlineSetup::handleDeliver(char* xml) {
175         mxml_node_t *tree;
176
177         // Determine xml type
178         tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK);
179         if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) {
180                 // Session XML
181                 gSessionData->parseSessionXML(xml);
182                 sendData(NULL, 0, RESPONSE_ACK);
183                 logg->logMessage("Received session xml");
184         } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) {
185                 // Configuration XML
186                 writeConfiguration(xml);
187                 sendData(NULL, 0, RESPONSE_ACK);
188                 logg->logMessage("Received configuration xml");
189         } else {
190                 // Unknown XML
191                 logg->logMessage("Received unknown XML delivery type");
192                 sendData(NULL, 0, RESPONSE_NAK);
193         }
194
195         mxmlDelete(tree);
196 }
197
198 void StreamlineSetup::sendData(const char* data, uint32_t length, char type) {
199         unsigned char header[5];
200         header[0] = type;
201         header[1] = (length >> 0) & 0xff;
202         header[2] = (length >> 8) & 0xff;
203         header[3] = (length >> 16) & 0xff;
204         header[4] = (length >> 24) & 0xff;
205         mSocket->send((char*)&header, sizeof(header));
206         mSocket->send((char*)data, length);
207 }
208
209 void StreamlineSetup::sendEvents() {
210         EventsXML eventsXML;
211         char* string = eventsXML.getXML();
212         sendString(string, RESPONSE_XML);
213         free(string);
214 }
215
216 void StreamlineSetup::sendConfiguration() {
217         ConfigurationXML xml;
218
219         const char* string = xml.getConfigurationXML();
220         sendData(string, strlen(string), RESPONSE_XML);
221 }
222
223 void StreamlineSetup::sendDefaults() {
224         // Send the config built into the binary
225         const char* xml;
226         unsigned int size;
227         ConfigurationXML::getDefaultConfigurationXml(xml, size);
228
229         // Artificial size restriction
230         if (size > 1024*1024) {
231                 logg->logError(__FILE__, __LINE__, "Corrupt default configuration file");
232                 handleException();
233         }
234
235         sendData(xml, size, RESPONSE_XML);
236 }
237
238 void StreamlineSetup::sendCounters() {
239         mxml_node_t *xml;
240         mxml_node_t *counters;
241
242         xml = mxmlNewXML("1.0");
243         counters = mxmlNewElement(xml, "counters");
244         for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
245                 driver->writeCounters(counters);
246         }
247
248         char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
249         sendString(string, RESPONSE_XML);
250
251         free(string);
252         mxmlDelete(xml);
253 }
254
255 void StreamlineSetup::writeConfiguration(char* xml) {
256         char path[PATH_MAX];
257
258         if (gSessionData->mConfigurationXMLPath) {
259                 strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX);
260         } else {
261                 util->getApplicationFullPath(path, PATH_MAX);
262                 strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1);
263         }
264
265         if (util->writeToDisk(path, xml) < 0) {
266                 logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path);
267                 handleException();
268         }
269
270         // Re-populate gSessionData with the configuration, as it has now changed
271         { ConfigurationXML configuration; }
272
273         if (gSessionData->mCounterOverflow > 0) {
274                 logg->logError(__FILE__, __LINE__, "Only %i performance counters counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
275                 handleException();
276         }
277 }