1 #include "crow/app.h" 2 #include "crow/ci_map.h" 3 #include "crow/common.h" 4 #include "crow/dumb_timer_queue.h" 5 #include "crow/http_connection.h" 6 #include "crow/http_parser_merged.h" 7 #include "crow/http_request.h" 8 #include "crow/http_response.h" 9 #include "crow/http_server.h" 10 #include "crow/json.h" 11 #include "crow/logging.h" 12 #include "crow/middleware.h" 13 #include "crow/middleware_context.h" 14 #include "crow/mustache.h" 15 #include "crow/parser.h" 16 #include "crow/query_string.h" 17 #include "crow/routing.h" 18 #include "crow/settings.h" 19 #include "crow/socket_adaptors.h" 20 #include "crow/utility.h" 21 #include "crow/websocket.h" 22 23 #include "color_cout_g3_sink.hpp" 24 #include "security_headers_middleware.hpp" 25 #include "ssl_key_handler.hpp" 26 #include "token_authorization_middleware.hpp" 27 #include "web_kvm.hpp" 28 #include "webassets.hpp" 29 30 #include <boost/asio.hpp> 31 #include <boost/endian/arithmetic.hpp> 32 33 #include <dbus/dbus.h> 34 #include <boost/iostreams/stream.hpp> 35 #include <boost/property_tree/ptree.hpp> 36 #include <boost/property_tree/xml_parser.hpp> 37 38 #include <iostream> 39 #include <memory> 40 #include <string> 41 #include <unordered_set> 42 43 using sensor_values = std::vector<std::pair<std::string, int32_t>>; 44 45 std::vector<std::string> read_dbus_xml_names(std::string& xml_data) { 46 std::vector<std::string> values; 47 // populate tree structure pt 48 using boost::property_tree::ptree; 49 ptree pt; 50 boost::iostreams::stream<boost::iostreams::array_source> stream( 51 xml_data.c_str(), xml_data.size()); 52 read_xml(stream, pt); 53 54 // traverse node to find other nodes 55 for (const auto& interface : pt.get_child("node")) { 56 if (interface.first == "node") { 57 auto t = interface.second.get<std::string>("<xmlattr>", "default"); 58 for (const auto& subnode : interface.second.get_child("<xmlattr>")) { 59 if (subnode.first == "name") { 60 auto t = subnode.second.get("", "unknown"); 61 values.emplace_back(std::move(t)); 62 } 63 } 64 } 65 } 66 return values; 67 } 68 69 sensor_values read_sensor_values() { 70 sensor_values values; 71 DBusError err; 72 73 int ret; 74 bool stat; 75 dbus_uint32_t level; 76 77 // initialiset the errors 78 dbus_error_init(&err); 79 80 // connect to the system bus and check for errors 81 DBusConnection* conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); 82 if (dbus_error_is_set(&err)) { 83 fprintf(stderr, "Connection Error (%s)\n", err.message); 84 dbus_error_free(&err); 85 } 86 if (NULL == conn) { 87 exit(1); 88 } 89 90 // create a new method call and check for errors 91 DBusMessage* msg = dbus_message_new_method_call( 92 "org.openbmc.Sensors", // target for the method call 93 "/org/openbmc/sensors/tach", // object to call on 94 "org.freedesktop.DBus.Introspectable", // interface to call on 95 "Introspect"); // method name 96 if (NULL == msg) { 97 fprintf(stderr, "Message Null\n"); 98 exit(1); 99 } 100 101 DBusPendingCall* pending; 102 // send message and get a handle for a reply 103 if (!dbus_connection_send_with_reply(conn, msg, &pending, 104 -1)) { // -1 is default timeout 105 fprintf(stderr, "Out Of Memory!\n"); 106 exit(1); 107 } 108 if (NULL == pending) { 109 fprintf(stderr, "Pending Call Null\n"); 110 exit(1); 111 } 112 dbus_connection_flush(conn); 113 114 // free message 115 dbus_message_unref(msg); 116 117 // block until we recieve a reply 118 dbus_pending_call_block(pending); 119 120 // get the reply message 121 msg = dbus_pending_call_steal_reply(pending); 122 if (NULL == msg) { 123 fprintf(stderr, "Reply Null\n"); 124 exit(1); 125 } 126 // free the pending message handle 127 dbus_pending_call_unref(pending); 128 129 // read the parameters 130 DBusMessageIter args; 131 char* xml_struct = NULL; 132 if (!dbus_message_iter_init(msg, &args)) { 133 fprintf(stderr, "Message has no arguments!\n"); 134 } 135 136 // read the arguments 137 if (!dbus_message_iter_init(msg, &args)) { 138 fprintf(stderr, "Message has no arguments!\n"); 139 } else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) { 140 fprintf(stderr, "Argument is not string!\n"); 141 } else { 142 dbus_message_iter_get_basic(&args, &xml_struct); 143 } 144 std::vector<std::string> methods; 145 if (xml_struct != NULL) { 146 std::string xml_data(xml_struct); 147 methods = read_dbus_xml_names(xml_data); 148 } 149 150 fprintf(stdout, "Found %zd sensors \n", methods.size()); 151 152 for (auto& method : methods) { 153 // TODO(Ed) make sure sensor exposes SensorValue interface 154 // create a new method call and check for errors 155 DBusMessage* msg = dbus_message_new_method_call( 156 "org.openbmc.Sensors", // target for the method call 157 ("/org/openbmc/sensors/tach/" + method).c_str(), // object to call on 158 "org.openbmc.SensorValue", // interface to call on 159 "getValue"); // method name 160 if (NULL == msg) { 161 fprintf(stderr, "Message Null\n"); 162 exit(1); 163 } 164 165 DBusPendingCall* pending; 166 // send message and get a handle for a reply 167 if (!dbus_connection_send_with_reply(conn, msg, &pending, 168 -1)) { // -1 is default timeout 169 fprintf(stderr, "Out Of Memory!\n"); 170 exit(1); 171 } 172 if (NULL == pending) { 173 fprintf(stderr, "Pending Call Null\n"); 174 exit(1); 175 } 176 dbus_connection_flush(conn); 177 178 // free message 179 dbus_message_unref(msg); 180 181 // block until we recieve a reply 182 dbus_pending_call_block(pending); 183 184 // get the reply message 185 msg = dbus_pending_call_steal_reply(pending); 186 if (NULL == msg) { 187 fprintf(stderr, "Reply Null\n"); 188 exit(1); 189 } 190 // free the pending message handle 191 dbus_pending_call_unref(pending); 192 193 // read the parameters 194 DBusMessageIter args; 195 int32_t value; 196 if (!dbus_message_iter_init(msg, &args)) { 197 fprintf(stderr, "Message has no arguments!\n"); 198 } 199 200 // read the arguments 201 if (!dbus_message_iter_init(msg, &args)) { 202 fprintf(stderr, "Message has no arguments!\n"); 203 } else if (DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type(&args)) { 204 fprintf(stderr, "Argument is not string!\n"); 205 } else { 206 DBusMessageIter sub; 207 dbus_message_iter_recurse(&args, &sub); 208 auto type = dbus_message_iter_get_arg_type(&sub); 209 if (DBUS_TYPE_INT32 != type) { 210 fprintf(stderr, "Variant subType is not int32 it is %d\n", type); 211 } else { 212 dbus_message_iter_get_basic(&sub, &value); 213 values.emplace_back(method.c_str(), value); 214 } 215 } 216 } 217 218 // free reply and close connection 219 dbus_message_unref(msg); 220 return values; 221 } 222 223 int main(int argc, char** argv) { 224 auto worker(g3::LogWorker::createLogWorker()); 225 if (false) { 226 auto handle = worker->addDefaultLogger("bmcweb", "/tmp/"); 227 } 228 g3::initializeLogging(worker.get()); 229 auto sink_handle = worker->addSink(std::make_unique<crow::ColorCoutSink>(), 230 &crow::ColorCoutSink::ReceiveLogMessage); 231 bool enable_ssl = true; 232 std::string ssl_pem_file("server.pem"); 233 234 if (enable_ssl) { 235 ensuressl::ensure_openssl_key_present_and_valid(ssl_pem_file); 236 } 237 238 crow::App< 239 crow::TokenAuthorizationMiddleware, crow::SecurityHeadersMiddleware> 240 app; 241 242 crow::webassets::request_routes(app); 243 crow::kvm::request_routes(app); 244 245 crow::logger::setLogLevel(crow::LogLevel::INFO); 246 CROW_ROUTE(app, "/systeminfo") 247 ([]() { 248 249 crow::json::wvalue j; 250 j["device_id"] = 0x7B; 251 j["device_provides_sdrs"] = true; 252 j["device_revision"] = true; 253 j["device_available"] = true; 254 j["firmware_revision"] = "0.68"; 255 256 j["ipmi_revision"] = "2.0"; 257 j["supports_chassis_device"] = true; 258 j["supports_bridge"] = true; 259 j["supports_ipmb_event_generator"] = true; 260 j["supports_ipmb_event_receiver"] = true; 261 j["supports_fru_inventory_device"] = true; 262 j["supports_sel_device"] = true; 263 j["supports_sdr_repository_device"] = true; 264 j["supports_sensor_device"] = true; 265 266 j["firmware_aux_revision"] = "0.60.foobar"; 267 268 return j; 269 }); 270 271 CROW_ROUTE(app, "/ipmiws") 272 .websocket() 273 .onopen([&](crow::websocket::connection& conn) { 274 275 }) 276 .onclose( 277 [&](crow::websocket::connection& conn, const std::string& reason) { 278 279 }) 280 .onmessage([&](crow::websocket::connection& conn, const std::string& data, 281 bool is_binary) { 282 boost::asio::io_service io_service; 283 using boost::asio::ip::udp; 284 udp::resolver resolver(io_service); 285 udp::resolver::query query(udp::v4(), "10.243.48.31", "623"); 286 udp::endpoint receiver_endpoint = *resolver.resolve(query); 287 288 udp::socket socket(io_service); 289 socket.open(udp::v4()); 290 291 socket.send_to(boost::asio::buffer(data), receiver_endpoint); 292 293 std::array<char, 255> recv_buf; 294 295 udp::endpoint sender_endpoint; 296 size_t len = 297 socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint); 298 // TODO(ed) THis is ugly. Find a way to not make a copy (ie, use 299 // std::string::data() to 300 std::string str(std::begin(recv_buf), std::end(recv_buf)); 301 LOG(DEBUG) << "Got " << str << "back \n"; 302 conn.send_binary(str); 303 304 }); 305 306 CROW_ROUTE(app, "/sensortest") 307 ([]() { 308 crow::json::wvalue j; 309 auto values = read_sensor_values(); 310 for (auto& pair : values) { 311 j[pair.first] = pair.second; 312 } 313 314 return j; 315 }); 316 317 CROW_ROUTE(app, "/intel/firmwareupload") 318 .methods("POST"_method)([](const crow::request& req) { 319 // TODO(ed) handle errors here (file exists already and is locked, ect) 320 std::ofstream out( 321 "/tmp/fw_update_image", 322 std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); 323 out << req.body; 324 out.close(); 325 326 crow::json::wvalue j; 327 j["status"] = "Upload Successfull"; 328 329 return j; 330 }); 331 332 LOG(DEBUG) << "Building SSL context"; 333 334 int port = 18080; 335 336 LOG(DEBUG) << "Starting webserver on port " << port; 337 app.port(port); 338 if (enable_ssl) { 339 LOG(DEBUG) << "SSL Enabled"; 340 auto ssl_context = ensuressl::get_ssl_context(ssl_pem_file); 341 app.ssl(std::move(ssl_context)); 342 } 343 app.concurrency(4); 344 app.run(); 345 } 346