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