Allow for systemd socket activation

If spawned via systemd's socket activation mechanism, use that socket
instead of opening a new one to listen on.

Change-Id: Ia35110902b30b08355edf2fe4041e8377582e72c
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ca29d9..591734b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -154,6 +154,7 @@
 target_link_libraries(bmcweb ${OPENSSL_LIBRARIES})
 target_link_libraries(bmcweb ${ZLIB_LIBRARIES})
 target_link_libraries(bmcweb pam)
+target_link_libraries(bmcweb -lsystemd)
 target_link_libraries(bmcweb -lstdc++fs)
 add_dependencies(bmcweb tinyxml2_static)
 target_link_libraries(bmcweb tinyxml2_static)
diff --git a/crow/include/crow/app.h b/crow/include/crow/app.h
index e00cf9d..c43faf3 100644
--- a/crow/include/crow/app.h
+++ b/crow/include/crow/app.h
@@ -54,6 +54,11 @@
     return router_.new_rule_tagged<Tag>(std::move(rule));
   }
 
+  self_t& socket(int existing_socket) {
+    socket_ = existing_socket;
+    return *this;
+  }
+
   self_t& port(std::uint16_t port) {
     port_ = port;
     return *this;
@@ -82,16 +87,27 @@
     validate();
 #ifdef CROW_ENABLE_SSL
     if (use_ssl_) {
-      ssl_server_ = std::move(
+      if (-1 == socket_) {
+        ssl_server_ = std::move(
           std::make_unique<ssl_server_t>(this, bindaddr_, port_, &middlewares_,
                                          concurrency_, &ssl_context_, io_));
+      } else {
+        ssl_server_ = std::move(
+          std::make_unique<ssl_server_t>(this, socket_, &middlewares_,
+                                         concurrency_, &ssl_context_, io_));
+      }
       ssl_server_->set_tick_function(tick_interval_, tick_function_);
       ssl_server_->run();
     } else
 #endif
     {
-      server_ = std::move(std::make_unique<server_t>(
-          this, bindaddr_, port_, &middlewares_, concurrency_, nullptr, io_));
+      if (-1 == socket_) {
+        server_ = std::move(std::make_unique<server_t>(
+            this, bindaddr_, port_, &middlewares_, concurrency_, nullptr, io_));
+      } else {
+        server_ = std::move(std::make_unique<server_t>(
+            this, socket_, &middlewares_, concurrency_, nullptr, io_));
+      }
       server_->set_tick_function(tick_interval_, tick_function_);
       server_->run();
     }
@@ -206,7 +222,8 @@
   std::shared_ptr<asio::io_service> io_;
   uint16_t port_ = 80;
   uint16_t concurrency_ = 1;
-  std::string bindaddr_ = "0.0.0.0";
+  std::string bindaddr_ = "::";
+  int socket_ = -1;
   Router router_;
 
   std::chrono::milliseconds tick_interval_{};
diff --git a/crow/include/crow/http_server.h b/crow/include/crow/http_server.h
index 7811743..54dd8a8 100644
--- a/crow/include/crow/http_server.h
+++ b/crow/include/crow/http_server.h
@@ -24,25 +24,45 @@
           typename... Middlewares>
 class Server {
  public:
-  Server(Handler* handler, const std::string& bindaddr, uint16_t port,
+  Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor,
          std::tuple<Middlewares...>* middlewares = nullptr,
          uint16_t concurrency = 1,
          typename Adaptor::context* adaptor_ctx = nullptr,
          std::shared_ptr<boost::asio::io_service> io =
              std::make_shared<boost::asio::io_service>())
       : io_service_(std::move(io)),
-        acceptor_(*io_service_,
-                  tcp::endpoint(boost::asio::ip::address::from_string(bindaddr),
-                                port)),
+        acceptor_(std::move(acceptor)),
         signals_(*io_service_, SIGINT, SIGTERM),
         tick_timer_(*io_service_),
         handler_(handler),
         concurrency_(concurrency),
-        port_(port),
-        bindaddr_(bindaddr),
         middlewares_(middlewares),
         adaptor_ctx_(adaptor_ctx) {}
 
+  Server(Handler* handler, const std::string& bindaddr, uint16_t port,
+         std::tuple<Middlewares...>* middlewares = nullptr,
+         uint16_t concurrency = 1,
+         typename Adaptor::context* adaptor_ctx = nullptr,
+         std::shared_ptr<boost::asio::io_service> io =
+             std::make_shared<boost::asio::io_service>())
+      : Server(handler,
+          std::make_unique<tcp::acceptor>(
+            *io, tcp::endpoint(
+              boost::asio::ip::address::from_string(bindaddr), port)),
+          middlewares, concurrency, adaptor_ctx, io) {}
+
+
+  Server(Handler* handler, int existing_socket,
+         std::tuple<Middlewares...>* middlewares = nullptr,
+         uint16_t concurrency = 1,
+         typename Adaptor::context* adaptor_ctx = nullptr,
+         std::shared_ptr<boost::asio::io_service> io =
+             std::make_shared<boost::asio::io_service>())
+      : Server(handler,
+          std::make_unique<tcp::acceptor>(
+            *io, boost::asio::ip::tcp::v6(), existing_socket),
+          middlewares, concurrency, adaptor_ctx, io) {}
+
   void set_tick_function(std::chrono::milliseconds d, std::function<void()> f) {
     tick_interval_ = d;
     tick_function_ = f;
@@ -147,7 +167,8 @@
       });
     }
 
-    CROW_LOG_INFO << server_name_ << " server is running, local port " << port_;
+    CROW_LOG_INFO << server_name_ << " server is running, local endpoint "
+                  << acceptor_->local_endpoint();
 
     signals_.async_wait([&](const boost::system::error_code& /*error*/,
                             int /*signal_number*/) { stop(); });
@@ -185,7 +206,7 @@
         is, handler_, server_name_, middlewares_,
         get_cached_date_str_pool_[roundrobin_index_],
         *timer_queue_pool_[roundrobin_index_], adaptor_ctx_);
-    acceptor_.async_accept(p->socket(),
+    acceptor_->async_accept(p->socket(),
                            [this, p, &is](boost::system::error_code ec) {
                              if (!ec) {
                                is.post([p] { p->start(); });
@@ -199,15 +220,13 @@
   std::vector<std::unique_ptr<asio::io_service>> io_service_pool_;
   std::vector<detail::dumb_timer_queue*> timer_queue_pool_;
   std::vector<std::function<std::string()>> get_cached_date_str_pool_;
-  tcp::acceptor acceptor_;
+  std::unique_ptr<tcp::acceptor> acceptor_;
   boost::asio::signal_set signals_;
   boost::asio::deadline_timer tick_timer_;
 
   Handler* handler_;
   uint16_t concurrency_{1};
   std::string server_name_ = "iBMC";
-  uint16_t port_;
-  std::string bindaddr_;
   unsigned int roundrobin_index_{};
 
   std::chrono::milliseconds tick_interval_{};
diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp
index 8060f88..834a5df 100644
--- a/src/webserver_main.cpp
+++ b/src/webserver_main.cpp
@@ -14,8 +14,31 @@
 #include <string>
 #include <crow/app.h>
 #include <boost/asio.hpp>
+#include <systemd/sd-daemon.h>
 #include "redfish.hpp"
 
+constexpr int defaultPort = 18080;
+
+template <typename... Middlewares>
+void setup_socket(crow::Crow<Middlewares...>& app) {
+  int listen_fd = sd_listen_fds(0);
+  if (1 == listen_fd) {
+    CROW_LOG_INFO << "attempting systemd socket activation";
+    if (sd_is_socket_inet(SD_LISTEN_FDS_START, AF_UNSPEC, SOCK_STREAM, 1, 0)) {
+      CROW_LOG_INFO << "Starting webserver on socket handle "
+                    << SD_LISTEN_FDS_START;
+      app.socket(SD_LISTEN_FDS_START);
+    } else {
+      CROW_LOG_INFO << "bad incoming socket, starting webserver on port "
+                << defaultPort;
+      app.port(defaultPort);
+    }
+  } else {
+    CROW_LOG_INFO << "Starting webserver on port " << defaultPort;
+    app.port(defaultPort);
+  }
+}
+
 int main(int argc, char** argv) {
   auto io = std::make_shared<boost::asio::io_service>();
   crow::App<crow::PersistentData::Middleware,
@@ -44,9 +67,9 @@
   crow::openbmc_mapper::request_routes(app);
 
   crow::logger::setLogLevel(crow::LogLevel::INFO);
-  int port = 18080;
-  std::cout << "Starting webserver on port " << port << "\n";
-  app.port(port);
+
+  CROW_LOG_INFO << "bmcweb (" << __DATE__ << ": " << __TIME__ << ')';
+  setup_socket(app);
 
   // Start dbus connection
   crow::connections::system_bus =