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