vlan: implement delete interface

Delete the in-memory vlan object,Also
deletes the associated device file and the
network file.

Change-Id: I613e31aaa4fa9172c6226765ac044481ffbd88ec
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp
index 30927f6..5eb6aa7 100644
--- a/ethernet_interface.cpp
+++ b/ethernet_interface.cpp
@@ -275,6 +275,56 @@
     writeConfigurationFile();
 }
 
+void EthernetInterface::deleteVLANObject(const std::string& interface)
+{
+    using namespace std::string_literals;
+
+    auto it = vlanInterfaces.find(interface);
+    if (it == vlanInterfaces.end())
+    {
+        log<level::ERR>("DeleteVLANObject:Unable to find the object",
+                         entry("INTERFACE=%s",interface.c_str()));
+        return;
+    }
+
+    auto confDir = manager.getConfDir();
+    fs::path networkFile = confDir;
+    networkFile /= systemd::config::networkFilePrefix + interface +
+                   systemd::config::networkFileSuffix;
+
+    fs::path deviceFile = confDir;
+    deviceFile /= interface + systemd::config::deviceFileSuffix;
+
+    // delete the vlan network file
+    if (fs::is_regular_file(networkFile))
+    {
+        fs::remove(networkFile);
+    }
+
+    // delete the vlan device file
+    if (fs::is_regular_file(deviceFile))
+    {
+        fs::remove(deviceFile);
+    }
+    // delete the interface
+    vlanInterfaces.erase(it);
+    // restart the systemd-networkd
+
+    restartSystemdUnit("systemd-networkd.service");
+
+    // TODO  systemd doesn't delete the virtual network interface
+    // even after deleting all the related configuartion.
+    // https://github.com/systemd/systemd/issues/6600
+    try
+    {
+        deleteInterface(interface);
+    }
+    catch (InternalFailure& e)
+    {
+        commit<InternalFailure>();
+    }
+}
+
 std::string EthernetInterface::generateObjectPath(IP::Protocol addressType,
                                                   const std::string& ipaddress,
                                                   uint8_t prefixLength,
@@ -371,7 +421,8 @@
     namespace fs = std::experimental::filesystem;
     fs::path confPath = manager.getConfDir();
 
-    std::string fileName = "00-bmc-"s + interfaceName() + ".network"s;
+    std::string fileName = systemd::config::networkFilePrefix + interfaceName() +
+                           systemd::config::networkFileSuffix;
     confPath /= fileName;
     std::fstream stream;
 
diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp
index 36669d6..29d377e 100644
--- a/ethernet_interface.hpp
+++ b/ethernet_interface.hpp
@@ -9,7 +9,6 @@
 
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server/object.hpp>
-
 #include <string>
 #include <experimental/filesystem>
 
@@ -94,6 +93,12 @@
          */
         void deleteObject(const std::string& ipaddress);
 
+        /* @brief delete the vlan dbus object of the given interface.
+         *        Also deletes the device file and the network file.
+         * @param[in] interface - VLAN Interface.
+         */
+        void deleteVLANObject(const std::string& interface);
+
         /* @brief creates the dbus object(IPaddres) given in the address list.
          * @param[in] addrs - address list for which dbus objects needs
          *                    to create.
diff --git a/types.hpp b/types.hpp
index ca35421..eb1f9fe 100644
--- a/types.hpp
+++ b/types.hpp
@@ -12,6 +12,17 @@
 {
 namespace network
 {
+namespace systemd
+{
+namespace config
+{
+
+constexpr auto networkFilePrefix = "00-bmc-";
+constexpr auto networkFileSuffix = ".network";
+constexpr auto deviceFileSuffix = ".netdev";
+
+}// namespace config
+}// namespace systemd
 
 using IntfName = std::string;
 
diff --git a/util.cpp b/util.cpp
index 64a913c..d96fe97 100644
--- a/util.cpp
+++ b/util.cpp
@@ -8,6 +8,7 @@
 #include <arpa/inet.h>
 #include <dirent.h>
 #include <net/if.h>
+#include <sys/wait.h>
 
 #include <iostream>
 #include <list>
@@ -18,6 +19,7 @@
 {
 namespace network
 {
+
 namespace
 {
 
@@ -52,7 +54,7 @@
         if (sscanf(str.c_str(), "%hx", &buff) <= 0)
         {
             log<level::ERR>("Invalid Mask",
-                             entry("SUBNETMASK=%s", subnetMask));
+                            entry("SUBNETMASK=%s", subnetMask));
 
             return 0;
         }
@@ -140,7 +142,7 @@
 }
 
 std::string getNetworkID(int addressFamily, const std::string& ipaddress,
-                       uint8_t prefix)
+                         uint8_t prefix)
 {
     unsigned char* pntMask = nullptr;
     unsigned char* pntNetwork = nullptr;
@@ -171,7 +173,7 @@
     if (inet_pton(addressFamily, ipaddress.c_str(), &ipaddressNetwork) <= 0)
     {
         log<level::ERR>("inet_pton failure",
-            entry("IPADDRESS=%s",ipaddress.c_str()));
+                        entry("IPADDRESS=%s", ipaddress.c_str()));
         report<InternalFailure>();
 
         return "";
@@ -202,13 +204,13 @@
     std::string linklocal = "fe80";
     return std::mismatch(linklocal.begin(), linklocal.end(),
                          address.begin()).first == linklocal.end() ?
-                            true : false;
+           true : false;
 }
 
 IntfAddrMap getInterfaceAddrs()
 {
-    IntfAddrMap intfMap{};
-    AddrList addrList{};
+    IntfAddrMap intfMap {};
+    AddrList addrList {};
     struct ifaddrs* ifaddr = nullptr;
 
     // attempt to fill struct with ifaddrs
@@ -223,7 +225,7 @@
     AddrPtr ifaddrPtr(ifaddr);
     ifaddr = nullptr;
 
-    std::string intfName{};
+    std::string intfName {};
 
     for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
     {
@@ -252,7 +254,7 @@
                 addrList.clear();
             }
             intfName = ifa->ifa_name;
-            AddrInfo info{};
+            AddrInfo info {};
             char ip[INET6_ADDRSTRLEN] = { 0 };
             char subnetMask[INET6_ADDRSTRLEN] = { 0 };
 
@@ -294,5 +296,48 @@
     return intfMap;
 }
 
+void deleteInterface(const std::string& intf)
+{
+    pid_t pid = fork();
+    int status {};
+
+    if (pid == 0)
+    {
+
+        execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr);
+        auto error = errno;
+        log<level::ERR>("Couldn't delete the device",
+                        entry("ERRNO=%d", error),
+                        entry("INTF=%s", intf.c_str()));
+        elog<InternalFailure>();
+    }
+    else if (pid < 0)
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during fork",
+                        entry("ERRNO=%d", error));
+        elog<InternalFailure>();
+    }
+    else if (pid > 0)
+    {
+        while (waitpid(pid, &status, 0) == -1)
+        {
+            if (errno != EINTR)
+            {   /* Error other than EINTR */
+                status = -1;
+                break;
+            }
+        }
+
+        if(status < 0)
+        {
+            log<level::ERR>("Unable to delete the interface",
+                             entry("INTF=%s", intf.c_str(),
+                             entry("STATUS=%d", status)));
+            elog<InternalFailure>();
+        }
+    }
+}
+
 }//namespace network
 }//namespace phosphor
diff --git a/util.hpp b/util.hpp
index 3434aca..1d74300 100644
--- a/util.hpp
+++ b/util.hpp
@@ -63,6 +63,12 @@
 
 }
 
+/** @brief Delete the given interface.
+ *  @param[in] intf - interface name.
+ */
+void deleteInterface(const std::string& intf);
+
+
 } //namespace network
 
 class Descriptor
diff --git a/vlan_interface.cpp b/vlan_interface.cpp
index f4224d4..88e3c10 100644
--- a/vlan_interface.cpp
+++ b/vlan_interface.cpp
@@ -26,14 +26,14 @@
                              uint32_t vlanID,
                              EthernetInterface& intf,
                              Manager& parent ) :
-        VlanIntfObject(bus, objPath.c_str(), true),
+        Interfaces(bus, objPath.c_str(), true),
         EthernetInterface(bus, objPath, dhcpEnabled, parent, false),
         parentInterface(intf)
 {
     id(vlanID);
     VlanIface::interfaceName(EthernetInterface::interfaceName());
 
-    VlanIntfObject::emit_object_added();
+    Interfaces::emit_object_added();
 }
 
 void VlanInterface::writeDeviceFile()
@@ -64,5 +64,10 @@
     stream.close();
 }
 
+void VlanInterface::delete_()
+{
+    parentInterface.deleteVLANObject(EthernetInterface::interfaceName());
+}
+
 }//namespace network
 }//namespace phosphor
diff --git a/vlan_interface.hpp b/vlan_interface.hpp
index c308611..3a06614 100644
--- a/vlan_interface.hpp
+++ b/vlan_interface.hpp
@@ -20,14 +20,16 @@
 class Manager;
 
 
+using DeleteIface = sdbusplus::xyz::openbmc_project::Object::server::Delete;
 using VlanIface = sdbusplus::xyz::openbmc_project::Network::server::VLAN;
-using VlanIntfObject =  sdbusplus::server::object::object<VlanIface>;
+using Interfaces =
+        sdbusplus::server::object::object<DeleteIface, VlanIface>;
 
 /** @class VlanInterface
  *  @brief OpenBMC vlan Interface implementation.
  *  @details A concrete implementation for the vlan interface
  */
-class VlanInterface : public VlanIntfObject, public EthernetInterface
+class VlanInterface : public Interfaces, public EthernetInterface
 {
     public:
         VlanInterface() = delete;
@@ -52,6 +54,10 @@
                       EthernetInterface& intf,
                       Manager& manager);
 
+        /** @brief Delete this d-bus object.
+         */
+        void delete_() override;
+
         /** @brief writes the device configuration.
                    systemd reads this configuration file
                    and creates the vlan interface.*/