Allow disabling SSH

The intent behind this commit is to add the dropbear service, monitor
the Enabled and Running properties, and then update the
dropbear.socket to allow disabling/enabling SSH.

Tested:
1.
busctl introspect xyz.openbmc_project.Control.Service.Manager /xyz/openbmc_project/control/service/dropbear
 xyz.openbmc_project.Control.Service.Attributes       interface
 .Enabled                property  b    false  emits-change writable
 .Masked                 property  b    false  emits-change writable
 .Running                property  b    true   emits-change writable

 Open a new SSH connection:
 sudo ssh username@<IP>
 Successfully connected

2.
busctl set-property xyz.openbmc_project.Control.Service.Manager /xyz/openbmc_project/control/service/dropbear xyz.openbmc_project.Control.Service.Attributes Running b false

busctl introspect xyz.openbmc_project.Control.Service.Manager /xyz/openbmc_project/control/service/dropbear
 xyz.openbmc_project.Control.Service.Attributes       interface
 .Enabled                property  b    false  emits-change writable
 .Masked                 property  b    false  emits-change writable
 .Running                property  b    false  emits-change writable

 Open a new SSH connection:
 sudo ssh username@<IP>
 ssh: connect to host <IP> port 22: Connection refused

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I5d004f7f49c25fc90b93dabd124fe0d3d93ca822
diff --git a/inc/srvcfg_manager.hpp b/inc/srvcfg_manager.hpp
index bf52ab2..4036623 100644
--- a/inc/srvcfg_manager.hpp
+++ b/inc/srvcfg_manager.hpp
@@ -91,6 +91,9 @@
     bool unitMaskedState = false;
     bool unitEnabledState = false;
     bool unitRunningState = false;
+
+    // dropbear is handled specially because it is a socket-activated service.
+    bool isDropBearService = false;
     std::string subStateValue;
 
     bool isMaskedOut();
diff --git a/inc/utils.hpp b/inc/utils.hpp
index c9df6ae..234124d 100644
--- a/inc/utils.hpp
+++ b/inc/utils.hpp
@@ -42,6 +42,26 @@
 static constexpr const char* stateEnabled = "enabled";
 static constexpr const char* stateDisabled = "disabled";
 static constexpr const char* subStateRunning = "running";
+static constexpr const char* subStateListening = "listening";
+
+using ListUnitsType =
+    std::tuple<std::string, std::string, std::string, std::string, std::string,
+               std::string, sdbusplus::message::object_path, uint32_t,
+               std::string, sdbusplus::message::object_path>;
+
+enum class ListUnitElements
+{
+    name,
+    descriptionString,
+    loadState,
+    activeState,
+    subState,
+    followedUnit,
+    objectPath,
+    queuedJobType,
+    jobType,
+    jobObject
+};
 
 static inline std::string addInstanceName(const std::string& instanceName,
                                           const std::string& suffix)
@@ -49,6 +69,9 @@
     return (instanceName.empty() ? "" : suffix + instanceName);
 }
 
+void checkAndThrowInternalFailure(boost::system::error_code& ec,
+                                  const std::string& msg);
+
 void systemdDaemonReload(
     const std::shared_ptr<sdbusplus::asio::connection>& conn,
     boost::asio::yield_context yield);
diff --git a/src/main.cpp b/src/main.cpp
index f8d1f22..1d9ebed 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -34,28 +34,9 @@
 
 // Base service name list. All instance of these services and
 // units(service/socket) will be managed by this daemon.
-static std::array<std::string, 5> serviceNames = {
-    "phosphor-ipmi-net", "bmcweb", "phosphor-ipmi-kcs", "start-ipkvm",
-    "obmc-console"};
-
-using ListUnitsType =
-    std::tuple<std::string, std::string, std::string, std::string, std::string,
-               std::string, sdbusplus::message::object_path, uint32_t,
-               std::string, sdbusplus::message::object_path>;
-
-enum class ListUnitElements
-{
-    name,
-    descriptionString,
-    loadState,
-    activeState,
-    subState,
-    followedUnit,
-    objectPath,
-    queuedJobType,
-    jobType,
-    jobObject
-};
+static std::array<std::string, 6> serviceNames = {
+    "phosphor-ipmi-net", "bmcweb",       "phosphor-ipmi-kcs",
+    "start-ipkvm",       "obmc-console", "dropbear"};
 
 enum class UnitType
 {
diff --git a/src/srvcfg_manager.cpp b/src/srvcfg_manager.cpp
index 038d11a..bd23b44 100644
--- a/src/srvcfg_manager.cpp
+++ b/src/srvcfg_manager.cpp
@@ -96,7 +96,8 @@
     if (subStateIt != propertyMap.end())
     {
         subStateValue = std::get<std::string>(subStateIt->second);
-        if (subStateValue == subStateRunning)
+        if (subStateValue == subStateRunning ||
+            subStateValue == subStateListening)
         {
             unitRunningState = true;
         }
@@ -111,6 +112,13 @@
 
 void ServiceConfig::queryAndUpdateProperties()
 {
+    std::string objectPath =
+        isDropBearService ? socketObjectPath : serviceObjectPath;
+    if (objectPath.empty())
+    {
+        return;
+    }
+
     conn->async_method_call(
         [this](boost::system::error_code ec,
                const boost::container::flat_map<std::string, VariantType>&
@@ -173,8 +181,7 @@
                 return;
             }
         },
-        sysdService, serviceObjectPath, dBusPropIntf, dBusGetAllMethod,
-        sysdUnitIntf);
+        sysdService, objectPath, dBusPropIntf, dBusGetAllMethod, sysdUnitIntf);
     return;
 }
 
@@ -213,6 +220,10 @@
     instanceName(instanceName_), serviceObjectPath(serviceObjPath_),
     socketObjectPath(socketObjPath_)
 {
+    if (baseUnitName == "dropbear")
+    {
+        isDropBearService = true;
+    }
     instantiatedUnitName = baseUnitName + addInstanceName(instanceName, "@");
     updatedFlag = 0;
     queryAndUpdateProperties();
@@ -247,13 +258,45 @@
     phosphor::logging::log<phosphor::logging::level::INFO>(
         "Applying new settings.",
         phosphor::logging::entry("OBJPATH=%s", objPath.c_str()));
-    if (subStateValue == "running")
+    if (subStateValue == subStateRunning || subStateValue == subStateListening)
     {
         if (!socketObjectPath.empty())
         {
             systemdUnitAction(conn, yield, getSocketUnitName(), sysdStopUnit);
         }
-        systemdUnitAction(conn, yield, getServiceUnitName(), sysdStopUnit);
+        if (!isDropBearService)
+        {
+            systemdUnitAction(conn, yield, getServiceUnitName(), sysdStopUnit);
+        }
+        else
+        {
+            // Get the ListUnits property, find all the services of
+            // `dropbear@<ip><port>.service` and stop the service through
+            // the systemdUnitAction method
+            boost::system::error_code ec;
+            auto listUnits =
+                conn->yield_method_call<std::vector<ListUnitsType>>(
+                    yield, ec, sysdService, sysdObjPath, sysdMgrIntf,
+                    "ListUnits");
+
+            checkAndThrowInternalFailure(
+                ec, "yield_method_call error: ListUnits failed");
+
+            for (const auto& unit : listUnits)
+            {
+                const auto& service =
+                    std::get<static_cast<int>(ListUnitElements::name)>(unit);
+                const auto& status =
+                    std::get<static_cast<int>(ListUnitElements::subState)>(
+                        unit);
+                if (service.find("dropbear@") != std::string::npos &&
+                    service.find(".service") != std::string::npos &&
+                    status == subStateRunning)
+                {
+                    systemdUnitAction(conn, yield, service, sysdStopUnit);
+                }
+            }
+        }
     }
 
     if (updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::port)))
@@ -297,6 +340,10 @@
         {
             unitFiles = {getServiceUnitName()};
         }
+        else if (!socketObjectPath.empty() && isDropBearService)
+        {
+            unitFiles = {getSocketUnitName()};
+        }
         else
         {
             unitFiles = {getSocketUnitName(), getServiceUnitName()};
@@ -321,7 +368,11 @@
             systemdUnitAction(conn, yield, getSocketUnitName(),
                               sysdRestartUnit);
         }
-        systemdUnitAction(conn, yield, getServiceUnitName(), sysdRestartUnit);
+        if (!isDropBearService)
+        {
+            systemdUnitAction(conn, yield, getServiceUnitName(),
+                              sysdRestartUnit);
+        }
     }
 
     // Reset the flag
diff --git a/src/utils.cpp b/src/utils.cpp
index b4ed9f7..0eba710 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -15,8 +15,8 @@
 */
 #include "utils.hpp"
 
-static inline void checkAndThrowInternalFailure(boost::system::error_code& ec,
-                                                const std::string& msg)
+void checkAndThrowInternalFailure(boost::system::error_code& ec,
+                                  const std::string& msg)
 {
     if (ec)
     {