PDUUnit.cpp 4.08 KB
Newer Older
1
2
3
4
5
6
7
8
9
/*
 * PDUUnit.cpp
 *
 *  Created on: 26.02.2018
 *      Author: Micha Mueller
 */

#include "PDUUnit.h"
#include "timestamp.h"
10
11
12
13

#include <iostream>
#include <sstream>

14
#include <boost/foreach.hpp>
15
16
#include <boost/property_tree/xml_parser.hpp>

17
18
19
20
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>

21
PDUUnit::PDUUnit() {
22
	_ttl = 1000;
23
	_lastRefresh = 0;
24
	_host = "";
25
	_strand = NULL;
26
27
28
}

PDUUnit::~PDUUnit() {
29
30
31
	if (_strand) {
		delete _strand;
	}
32
33
}

34
uint64_t PDUUnit::readValue(const xmlPathVector_t& xmlPath) {
35
	uint64_t now = getTimestamp();
36
	if (now >= _lastRefresh + MS_TO_NS(_ttl)) {
37
38
		refresh();
#ifdef DEBUG
Micha Mueller's avatar
Micha Mueller committed
39
		LOG(debug) << "PDUUnit " << _host << " refreshed XML-file";
40
#endif
41
	}
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

	std::string reading;
	boost::property_tree::ptree node = _ptree;
	for (size_t i = 0; i < xmlPath.size(); i++) {
		const std::string& path = std::get<0>(xmlPath[i]);
		const std::string& child = std::get<1>(xmlPath[i]);
		const attributesVector_t& attVec = std::get<2>(xmlPath[i]);

		unsigned matchCount;
		if (child != "") {
			BOOST_FOREACH(boost::property_tree::ptree::value_type &v, node.get_child(path)) {
				if (v.first == child) {
					matchCount = 0;
					for (size_t j = 0; j < attVec.size(); j++) {
						std::string attributeVal = v.second.get_child("<xmlattr>." + attVec[j].first).data();

						if (attributeVal != attVec[j].second) { //attribute values don't match
							break;
						} else {
							matchCount++;
62
63
						}
					}
64
65
66
67
68
					if (matchCount == attVec.size()) { //all attributes matched
						reading = v.second.data();
						node = v.second;
						break;
					}
69
70
				}
			}
71
72
73
		} else {	//child == ""
			reading = node.get(path, "");
			break;	//last (part of the) path
74
75
		}
	}
76
77

	if (reading == "") {
78
		throw std::runtime_error("Value not found!");
79
80
	}
#ifdef DEBUG
81
	LOG(debug) << "Read: " << reading;
82
83
#endif
	return stoul(reading);
84
85
}

Micha Mueller's avatar
Micha Mueller committed
86
void PDUUnit::initializeStrand(boost::asio::io_service& io) {
87
88
89
	if (!_strand) {
		_strand = new boost::asio::io_service::strand(io);
	}
90
91
}

92
void PDUUnit::refresh() {
93
94
	SSL_library_init();
	SSL_load_error_strings();
95
	ERR_load_BIO_strings();
96
97
98
99
100
101
102
103
	OpenSSL_add_all_algorithms();

	SSL_CTX*	ctx;
	BIO*		bio;

	ctx = SSL_CTX_new(SSLv23_method());

	if (!ctx) {
104
		LOG(error) << "OpenSSL: Could not create context: " << ERR_reason_error_string(ERR_get_error());
105
106
107
		return;
	}

108
109
	/*
	if (!SSL_CTX_load_verify_locations(ctx, "/home/micha/LRZ/dcdbOwnFork/deps/openssl-1.0.2l/certs/demo/ca-cert.pem", NULL)) {
110
		LOG(error) << "OpenSSL: Could not load certificate: " << ERR_reason_error_string(ERR_get_error());
111
112
113
114
115
		SSL_CTX_free(ctx);
		return;
	}
	*/

116
117
	bio = BIO_new_ssl_connect(ctx);

118
	BIO_set_conn_hostname(bio, _host.c_str());
119
120
121

	if(BIO_do_connect(bio) <= 0)
	{
122
		LOG(error) << "OpenSSL: Could not connect: " << ERR_reason_error_string(ERR_get_error());
123
124
125
126
127
	    BIO_free_all(bio);
	    SSL_CTX_free(ctx);
	    return;
	}

128
129
130
131
132
	size_t len = _request.length();
	const char* reqBuf = _request.c_str();	//request

	char resBuf[2048];	//response
	memset(resBuf, 0, 2048);
133
134
135
136
137

	// do not bother too long if a write/read failed. Sensor intervals are usually small, just try again when next sensor wants to read.
	// Current sensor has to use old value though.

	// send request
138
	if (BIO_write(bio, reqBuf, len) <= 0) {
139
		LOG(error) << "OpenSSL: Could not send request: " << ERR_reason_error_string(ERR_get_error());
140
141
142
		BIO_free_all(bio);
		SSL_CTX_free(ctx);
		return;
143
	}
144
145

	// read reply
146
147
148
149
150
151
152
153
154
155
156
157
	std::string response;
	while ((len = BIO_read(bio, resBuf, sizeof(resBuf))) > 0) {
                resBuf[len] = 0;
                response.append(resBuf);
        }

	if (len < 0) {
                std::cerr << "OpenSSL: Could not read response: " << ERR_reason_error_string(ERR_get_error()) << std::endl;
                BIO_free_all(bio);
                SSL_CTX_free(ctx);
                return;
        }
158
159
160
161

	BIO_free_all(bio);
	SSL_CTX_free(ctx);

162
163
	std::string xml = response.substr(response.find("<"));
	std::istringstream treeStream(xml);
164
165
166
167
168
169
	try {
		boost::property_tree::read_xml(treeStream, _ptree);
	} catch (const std::exception& e) {
		LOG(error) << "PDU: Got malformed XML repsonse from " << _host;
		return;
	}
170
	_lastRefresh = getTimestamp();
171
	return;
172
173
}