11bbe3d1eSWilliam A. Kennington III #include "config.h" 21bbe3d1eSWilliam A. Kennington III 31bbe3d1eSWilliam A. Kennington III #include "network_manager.hpp" 41bbe3d1eSWilliam A. Kennington III #include "rtnetlink_server.hpp" 51bbe3d1eSWilliam A. Kennington III #include "types.hpp" 61bbe3d1eSWilliam A. Kennington III #include "watch.hpp" 71bbe3d1eSWilliam A. Kennington III 81bbe3d1eSWilliam A. Kennington III #include <linux/netlink.h> 91bbe3d1eSWilliam A. Kennington III 101bbe3d1eSWilliam A. Kennington III #include <filesystem> 111bbe3d1eSWilliam A. Kennington III #include <fstream> 121bbe3d1eSWilliam A. Kennington III #include <functional> 131bbe3d1eSWilliam A. Kennington III #include <memory> 141bbe3d1eSWilliam A. Kennington III #ifdef SYNC_MAC_FROM_INVENTORY 151bbe3d1eSWilliam A. Kennington III #include <nlohmann/json.hpp> 161bbe3d1eSWilliam A. Kennington III #endif 171bbe3d1eSWilliam A. Kennington III #include <phosphor-logging/elog-errors.hpp> 181bbe3d1eSWilliam A. Kennington III #include <phosphor-logging/log.hpp> 191bbe3d1eSWilliam A. Kennington III #include <sdbusplus/bus.hpp> 201bbe3d1eSWilliam A. Kennington III #include <sdbusplus/bus/match.hpp> 211bbe3d1eSWilliam A. Kennington III #include <sdbusplus/server/manager.hpp> 221bbe3d1eSWilliam A. Kennington III #include <sdeventplus/event.hpp> 231bbe3d1eSWilliam A. Kennington III #include <xyz/openbmc_project/Common/error.hpp> 241bbe3d1eSWilliam A. Kennington III 251bbe3d1eSWilliam A. Kennington III using phosphor::logging::elog; 261bbe3d1eSWilliam A. Kennington III using phosphor::logging::entry; 271bbe3d1eSWilliam A. Kennington III using phosphor::logging::level; 281bbe3d1eSWilliam A. Kennington III using phosphor::logging::log; 291bbe3d1eSWilliam A. Kennington III using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 301bbe3d1eSWilliam A. Kennington III using DbusObjectPath = std::string; 311bbe3d1eSWilliam A. Kennington III using DbusInterface = std::string; 321bbe3d1eSWilliam A. Kennington III using PropertyValue = std::string; 331bbe3d1eSWilliam A. Kennington III 341bbe3d1eSWilliam A. Kennington III constexpr char NETWORK_CONF_DIR[] = "/etc/systemd/network"; 351bbe3d1eSWilliam A. Kennington III 361bbe3d1eSWilliam A. Kennington III constexpr char DEFAULT_OBJPATH[] = "/xyz/openbmc_project/network"; 371bbe3d1eSWilliam A. Kennington III 381bbe3d1eSWilliam A. Kennington III constexpr auto firstBootPath = "/var/lib/network/firstBoot_"; 391bbe3d1eSWilliam A. Kennington III constexpr auto configFile = "/usr/share/network/config.json"; 401bbe3d1eSWilliam A. Kennington III 411bbe3d1eSWilliam A. Kennington III constexpr auto invNetworkIntf = 421bbe3d1eSWilliam A. Kennington III "xyz.openbmc_project.Inventory.Item.NetworkInterface"; 431bbe3d1eSWilliam A. Kennington III 441bbe3d1eSWilliam A. Kennington III namespace phosphor 451bbe3d1eSWilliam A. Kennington III { 461bbe3d1eSWilliam A. Kennington III namespace network 471bbe3d1eSWilliam A. Kennington III { 481bbe3d1eSWilliam A. Kennington III 491bbe3d1eSWilliam A. Kennington III std::unique_ptr<phosphor::network::Manager> manager = nullptr; 501bbe3d1eSWilliam A. Kennington III std::unique_ptr<Timer> refreshObjectTimer = nullptr; 51c7cf25f7SWilliam A. Kennington III std::unique_ptr<Timer> reloadTimer = nullptr; 521bbe3d1eSWilliam A. Kennington III 531bbe3d1eSWilliam A. Kennington III #ifdef SYNC_MAC_FROM_INVENTORY 54c38b0710SPatrick Williams std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr; 551bbe3d1eSWilliam A. Kennington III std::vector<std::string> first_boot_status; 561bbe3d1eSWilliam A. Kennington III 57c38b0710SPatrick Williams bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, 581bbe3d1eSWilliam A. Kennington III const nlohmann::json& configJson, 591bbe3d1eSWilliam A. Kennington III const std::string& intfname) 601bbe3d1eSWilliam A. Kennington III { 611bbe3d1eSWilliam A. Kennington III try 621bbe3d1eSWilliam A. Kennington III { 631bbe3d1eSWilliam A. Kennington III auto inventoryMAC = mac_address::getfromInventory(bus, intfname); 641bbe3d1eSWilliam A. Kennington III if (!mac_address::toString(inventoryMAC).empty()) 651bbe3d1eSWilliam A. Kennington III { 661bbe3d1eSWilliam A. Kennington III log<level::INFO>("Mac Address in Inventory on "), 671bbe3d1eSWilliam A. Kennington III entry("Interface : ", intfname.c_str()), 681bbe3d1eSWilliam A. Kennington III entry("MAC Address :", 691bbe3d1eSWilliam A. Kennington III (mac_address::toString(inventoryMAC)).c_str()); 701bbe3d1eSWilliam A. Kennington III manager->setFistBootMACOnInterface(std::make_pair( 711bbe3d1eSWilliam A. Kennington III intfname.c_str(), mac_address::toString(inventoryMAC))); 721bbe3d1eSWilliam A. Kennington III first_boot_status.push_back(intfname.c_str()); 731bbe3d1eSWilliam A. Kennington III bool status = true; 741bbe3d1eSWilliam A. Kennington III for (const auto& keys : configJson.items()) 751bbe3d1eSWilliam A. Kennington III { 761bbe3d1eSWilliam A. Kennington III if (!(std::find(first_boot_status.begin(), 771bbe3d1eSWilliam A. Kennington III first_boot_status.end(), 781bbe3d1eSWilliam A. Kennington III keys.key()) != first_boot_status.end())) 791bbe3d1eSWilliam A. Kennington III { 801bbe3d1eSWilliam A. Kennington III log<level::INFO>("Interface MAC is NOT set from VPD"), 811bbe3d1eSWilliam A. Kennington III entry("INTERFACE", keys.key().c_str()); 821bbe3d1eSWilliam A. Kennington III status = false; 831bbe3d1eSWilliam A. Kennington III } 841bbe3d1eSWilliam A. Kennington III } 851bbe3d1eSWilliam A. Kennington III if (status) 861bbe3d1eSWilliam A. Kennington III { 871bbe3d1eSWilliam A. Kennington III log<level::INFO>("Removing the match for ethernet interfaces"); 881bbe3d1eSWilliam A. Kennington III phosphor::network::EthInterfaceMatch = nullptr; 891bbe3d1eSWilliam A. Kennington III } 901bbe3d1eSWilliam A. Kennington III } 911bbe3d1eSWilliam A. Kennington III else 921bbe3d1eSWilliam A. Kennington III { 931bbe3d1eSWilliam A. Kennington III log<level::INFO>("Nothing is present in Inventory"); 941bbe3d1eSWilliam A. Kennington III return false; 951bbe3d1eSWilliam A. Kennington III } 961bbe3d1eSWilliam A. Kennington III } 971bbe3d1eSWilliam A. Kennington III catch (const std::exception& e) 981bbe3d1eSWilliam A. Kennington III { 991bbe3d1eSWilliam A. Kennington III log<level::ERR>("Exception occurred during getting of MAC " 1001bbe3d1eSWilliam A. Kennington III "address from Inventory"); 1011bbe3d1eSWilliam A. Kennington III return false; 1021bbe3d1eSWilliam A. Kennington III } 1031bbe3d1eSWilliam A. Kennington III return true; 1041bbe3d1eSWilliam A. Kennington III } 1051bbe3d1eSWilliam A. Kennington III 1061bbe3d1eSWilliam A. Kennington III // register the macthes to be monitored from inventory manager 107c38b0710SPatrick Williams void registerSignals(sdbusplus::bus_t& bus, const nlohmann::json& configJson) 1081bbe3d1eSWilliam A. Kennington III { 1091bbe3d1eSWilliam A. Kennington III log<level::INFO>("Registering the Inventory Signals Matcher"); 1101bbe3d1eSWilliam A. Kennington III 111c38b0710SPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch; 1121bbe3d1eSWilliam A. Kennington III 113c38b0710SPatrick Williams auto callback = [&](sdbusplus::message_t& m) { 1141bbe3d1eSWilliam A. Kennington III std::map<DbusObjectPath, 1151bbe3d1eSWilliam A. Kennington III std::map<DbusInterface, std::variant<PropertyValue>>> 1161bbe3d1eSWilliam A. Kennington III interfacesProperties; 1171bbe3d1eSWilliam A. Kennington III 1181bbe3d1eSWilliam A. Kennington III sdbusplus::message::object_path objPath; 1191bbe3d1eSWilliam A. Kennington III std::pair<std::string, std::string> ethPair; 1201bbe3d1eSWilliam A. Kennington III m.read(objPath, interfacesProperties); 1211bbe3d1eSWilliam A. Kennington III 1221bbe3d1eSWilliam A. Kennington III for (const auto& pattern : configJson.items()) 1231bbe3d1eSWilliam A. Kennington III { 1241bbe3d1eSWilliam A. Kennington III if (objPath.str.find(pattern.value()) != std::string::npos) 1251bbe3d1eSWilliam A. Kennington III { 1261bbe3d1eSWilliam A. Kennington III for (auto& interface : interfacesProperties) 1271bbe3d1eSWilliam A. Kennington III { 1281bbe3d1eSWilliam A. Kennington III if (interface.first == invNetworkIntf) 1291bbe3d1eSWilliam A. Kennington III { 1301bbe3d1eSWilliam A. Kennington III for (const auto& property : interface.second) 1311bbe3d1eSWilliam A. Kennington III { 1321bbe3d1eSWilliam A. Kennington III if (property.first == "MACAddress") 1331bbe3d1eSWilliam A. Kennington III { 1341bbe3d1eSWilliam A. Kennington III ethPair = std::make_pair( 1351bbe3d1eSWilliam A. Kennington III pattern.key(), 1361bbe3d1eSWilliam A. Kennington III std::get<std::string>(property.second)); 1371bbe3d1eSWilliam A. Kennington III break; 1381bbe3d1eSWilliam A. Kennington III } 1391bbe3d1eSWilliam A. Kennington III } 1401bbe3d1eSWilliam A. Kennington III break; 1411bbe3d1eSWilliam A. Kennington III } 1421bbe3d1eSWilliam A. Kennington III } 1431bbe3d1eSWilliam A. Kennington III if (!(ethPair.first.empty() || ethPair.second.empty())) 1441bbe3d1eSWilliam A. Kennington III { 1451bbe3d1eSWilliam A. Kennington III manager->setFistBootMACOnInterface(ethPair); 1461bbe3d1eSWilliam A. Kennington III } 1471bbe3d1eSWilliam A. Kennington III } 1481bbe3d1eSWilliam A. Kennington III } 1491bbe3d1eSWilliam A. Kennington III }; 1501bbe3d1eSWilliam A. Kennington III 151c38b0710SPatrick Williams MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>( 1521bbe3d1eSWilliam A. Kennington III bus, 1531bbe3d1eSWilliam A. Kennington III "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 1541bbe3d1eSWilliam A. Kennington III "member='InterfacesAdded',path='/xyz/openbmc_project/" 1551bbe3d1eSWilliam A. Kennington III "inventory'", 1561bbe3d1eSWilliam A. Kennington III callback); 1571bbe3d1eSWilliam A. Kennington III } 1581bbe3d1eSWilliam A. Kennington III 159c38b0710SPatrick Williams void watchEthernetInterface(sdbusplus::bus_t& bus, 1601bbe3d1eSWilliam A. Kennington III const nlohmann::json& configJson) 1611bbe3d1eSWilliam A. Kennington III { 162c38b0710SPatrick Williams auto mycallback = [&](sdbusplus::message_t& m) { 1631bbe3d1eSWilliam A. Kennington III std::map<DbusObjectPath, 1641bbe3d1eSWilliam A. Kennington III std::map<DbusInterface, std::variant<PropertyValue>>> 1651bbe3d1eSWilliam A. Kennington III interfacesProperties; 1661bbe3d1eSWilliam A. Kennington III 1671bbe3d1eSWilliam A. Kennington III sdbusplus::message::object_path objPath; 1681bbe3d1eSWilliam A. Kennington III std::pair<std::string, std::string> ethPair; 1691bbe3d1eSWilliam A. Kennington III m.read(objPath, interfacesProperties); 1701bbe3d1eSWilliam A. Kennington III for (const auto& interfaces : interfacesProperties) 1711bbe3d1eSWilliam A. Kennington III { 1721bbe3d1eSWilliam A. Kennington III if (interfaces.first == 1731bbe3d1eSWilliam A. Kennington III "xyz.openbmc_project.Network.EthernetInterface") 1741bbe3d1eSWilliam A. Kennington III { 1751bbe3d1eSWilliam A. Kennington III for (const auto& property : interfaces.second) 1761bbe3d1eSWilliam A. Kennington III { 1771bbe3d1eSWilliam A. Kennington III if (property.first == "InterfaceName") 1781bbe3d1eSWilliam A. Kennington III { 1791bbe3d1eSWilliam A. Kennington III std::string infname = 1801bbe3d1eSWilliam A. Kennington III std::get<std::string>(property.second); 1811bbe3d1eSWilliam A. Kennington III 1821bbe3d1eSWilliam A. Kennington III if (configJson.find(infname) == configJson.end()) 1831bbe3d1eSWilliam A. Kennington III { 1841bbe3d1eSWilliam A. Kennington III // ethernet interface not found in configJSON 1851bbe3d1eSWilliam A. Kennington III // check if it is not sit0 interface, as it is 1861bbe3d1eSWilliam A. Kennington III // expected. 1871bbe3d1eSWilliam A. Kennington III if (infname != "sit0") 1881bbe3d1eSWilliam A. Kennington III { 1891bbe3d1eSWilliam A. Kennington III log<level::ERR>( 1901bbe3d1eSWilliam A. Kennington III "Wrong Interface Name in Config Json"); 1911bbe3d1eSWilliam A. Kennington III } 1921bbe3d1eSWilliam A. Kennington III } 1931bbe3d1eSWilliam A. Kennington III else 1941bbe3d1eSWilliam A. Kennington III { 1951bbe3d1eSWilliam A. Kennington III if (!phosphor::network::setInventoryMACOnSystem( 1961bbe3d1eSWilliam A. Kennington III bus, configJson, infname)) 1971bbe3d1eSWilliam A. Kennington III { 1981bbe3d1eSWilliam A. Kennington III phosphor::network::registerSignals(bus, 1991bbe3d1eSWilliam A. Kennington III configJson); 2001bbe3d1eSWilliam A. Kennington III phosphor::network::EthInterfaceMatch = nullptr; 2011bbe3d1eSWilliam A. Kennington III } 2021bbe3d1eSWilliam A. Kennington III } 2031bbe3d1eSWilliam A. Kennington III break; 2041bbe3d1eSWilliam A. Kennington III } 2051bbe3d1eSWilliam A. Kennington III } 2061bbe3d1eSWilliam A. Kennington III break; 2071bbe3d1eSWilliam A. Kennington III } 2081bbe3d1eSWilliam A. Kennington III } 2091bbe3d1eSWilliam A. Kennington III }; 2101bbe3d1eSWilliam A. Kennington III // Incase if phosphor-inventory-manager started early and the VPD is already 2111bbe3d1eSWilliam A. Kennington III // collected by the time network service has come up, better to check the 2121bbe3d1eSWilliam A. Kennington III // VPD directly and set the MAC Address on the respective Interface. 2131bbe3d1eSWilliam A. Kennington III 2141bbe3d1eSWilliam A. Kennington III bool registeredSignals = false; 2151bbe3d1eSWilliam A. Kennington III for (const auto& interfaceString : configJson.items()) 2161bbe3d1eSWilliam A. Kennington III { 2171bbe3d1eSWilliam A. Kennington III if (!std::filesystem::exists(firstBootPath + interfaceString.key()) && 2181bbe3d1eSWilliam A. Kennington III !registeredSignals) 2191bbe3d1eSWilliam A. Kennington III { 2201bbe3d1eSWilliam A. Kennington III 2211bbe3d1eSWilliam A. Kennington III log<level::INFO>( 2221bbe3d1eSWilliam A. Kennington III "First boot file is not present, check VPD for MAC"); 2231bbe3d1eSWilliam A. Kennington III phosphor::network::EthInterfaceMatch = std::make_unique< 224c38b0710SPatrick Williams sdbusplus::bus::match_t>( 2251bbe3d1eSWilliam A. Kennington III bus, 2261bbe3d1eSWilliam A. Kennington III "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 2271bbe3d1eSWilliam A. Kennington III "member='InterfacesAdded',path='/xyz/openbmc_project/network'", 2281bbe3d1eSWilliam A. Kennington III mycallback); 2291bbe3d1eSWilliam A. Kennington III registeredSignals = true; 2301bbe3d1eSWilliam A. Kennington III } 2311bbe3d1eSWilliam A. Kennington III } 2321bbe3d1eSWilliam A. Kennington III } 2331bbe3d1eSWilliam A. Kennington III 2341bbe3d1eSWilliam A. Kennington III #endif 2351bbe3d1eSWilliam A. Kennington III 2361bbe3d1eSWilliam A. Kennington III /** @brief refresh the network objects. */ 2371bbe3d1eSWilliam A. Kennington III void refreshObjects() 2381bbe3d1eSWilliam A. Kennington III { 2391bbe3d1eSWilliam A. Kennington III if (manager) 2401bbe3d1eSWilliam A. Kennington III { 2411bbe3d1eSWilliam A. Kennington III log<level::INFO>("Refreshing the objects."); 2421bbe3d1eSWilliam A. Kennington III manager->createChildObjects(); 2431bbe3d1eSWilliam A. Kennington III log<level::INFO>("Refreshing complete."); 2441bbe3d1eSWilliam A. Kennington III } 2451bbe3d1eSWilliam A. Kennington III } 2461bbe3d1eSWilliam A. Kennington III 247c7cf25f7SWilliam A. Kennington III void reloadNetworkd() 248c7cf25f7SWilliam A. Kennington III { 249c7cf25f7SWilliam A. Kennington III if (manager) 250c7cf25f7SWilliam A. Kennington III { 251c7cf25f7SWilliam A. Kennington III log<level::INFO>("Sending networkd reload"); 252c7cf25f7SWilliam A. Kennington III manager->doReloadConfigs(); 253c7cf25f7SWilliam A. Kennington III log<level::INFO>("Done networkd reload"); 254c7cf25f7SWilliam A. Kennington III } 255c7cf25f7SWilliam A. Kennington III } 256c7cf25f7SWilliam A. Kennington III 2571bbe3d1eSWilliam A. Kennington III void initializeTimers() 2581bbe3d1eSWilliam A. Kennington III { 2591bbe3d1eSWilliam A. Kennington III auto event = sdeventplus::Event::get_default(); 2601bbe3d1eSWilliam A. Kennington III refreshObjectTimer = 2611bbe3d1eSWilliam A. Kennington III std::make_unique<Timer>(event, std::bind(refreshObjects)); 262c7cf25f7SWilliam A. Kennington III reloadTimer = std::make_unique<Timer>(event, std::bind(reloadNetworkd)); 2631bbe3d1eSWilliam A. Kennington III } 2641bbe3d1eSWilliam A. Kennington III 2651bbe3d1eSWilliam A. Kennington III } // namespace network 2661bbe3d1eSWilliam A. Kennington III } // namespace phosphor 2671bbe3d1eSWilliam A. Kennington III 2681bbe3d1eSWilliam A. Kennington III int main(int /*argc*/, char** /*argv*/) 2691bbe3d1eSWilliam A. Kennington III { 2701bbe3d1eSWilliam A. Kennington III phosphor::network::initializeTimers(); 2711bbe3d1eSWilliam A. Kennington III 2721bbe3d1eSWilliam A. Kennington III auto bus = sdbusplus::bus::new_default(); 2731bbe3d1eSWilliam A. Kennington III 2741bbe3d1eSWilliam A. Kennington III // Need sd_event to watch for OCC device errors 2751bbe3d1eSWilliam A. Kennington III sd_event* event = nullptr; 2761bbe3d1eSWilliam A. Kennington III auto r = sd_event_default(&event); 2771bbe3d1eSWilliam A. Kennington III if (r < 0) 2781bbe3d1eSWilliam A. Kennington III { 2791bbe3d1eSWilliam A. Kennington III log<level::ERR>("Error creating a default sd_event handler"); 2801bbe3d1eSWilliam A. Kennington III return r; 2811bbe3d1eSWilliam A. Kennington III } 2821bbe3d1eSWilliam A. Kennington III 2831bbe3d1eSWilliam A. Kennington III phosphor::network::EventPtr eventPtr{event}; 2841bbe3d1eSWilliam A. Kennington III event = nullptr; 2851bbe3d1eSWilliam A. Kennington III 2861bbe3d1eSWilliam A. Kennington III // Attach the bus to sd_event to service user requests 2871bbe3d1eSWilliam A. Kennington III bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL); 2881bbe3d1eSWilliam A. Kennington III 2891bbe3d1eSWilliam A. Kennington III // Add sdbusplus Object Manager for the 'root' path of the network manager. 290c38b0710SPatrick Williams sdbusplus::server::manager_t objManager(bus, DEFAULT_OBJPATH); 2911bbe3d1eSWilliam A. Kennington III bus.request_name(DEFAULT_BUSNAME); 2921bbe3d1eSWilliam A. Kennington III 2931bbe3d1eSWilliam A. Kennington III phosphor::network::manager = std::make_unique<phosphor::network::Manager>( 2941bbe3d1eSWilliam A. Kennington III bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR); 2951bbe3d1eSWilliam A. Kennington III 2961bbe3d1eSWilliam A. Kennington III // create the default network files if the network file 2971bbe3d1eSWilliam A. Kennington III // is not there for any interface. 2981bbe3d1eSWilliam A. Kennington III // Parameter false means don't create the network 2991bbe3d1eSWilliam A. Kennington III // files forcefully. 300bd649af9SWilliam A. Kennington III if (phosphor::network::manager->createDefaultNetworkFiles(false)) 3011bbe3d1eSWilliam A. Kennington III { 302bd649af9SWilliam A. Kennington III phosphor::network::manager->reloadConfigs(); 3031bbe3d1eSWilliam A. Kennington III } 3041bbe3d1eSWilliam A. Kennington III 3051bbe3d1eSWilliam A. Kennington III // RTNETLINK event handler 306*32eef716SWilliam A. Kennington III phosphor::network::rtnetlink::Server svr(eventPtr); 3071bbe3d1eSWilliam A. Kennington III 3081bbe3d1eSWilliam A. Kennington III #ifdef SYNC_MAC_FROM_INVENTORY 3091bbe3d1eSWilliam A. Kennington III std::ifstream in(configFile); 3101bbe3d1eSWilliam A. Kennington III nlohmann::json configJson; 3111bbe3d1eSWilliam A. Kennington III in >> configJson; 3121bbe3d1eSWilliam A. Kennington III phosphor::network::watchEthernetInterface(bus, configJson); 3131bbe3d1eSWilliam A. Kennington III #endif 314bd649af9SWilliam A. Kennington III 315bd649af9SWilliam A. Kennington III // Trigger the initial object scan 31626c40a43SWilliam A. Kennington III // This is intentionally deferred, to ensure that systemd-networkd is 31726c40a43SWilliam A. Kennington III // fully configured. 31826c40a43SWilliam A. Kennington III phosphor::network::refreshObjectTimer->restartOnce( 31926c40a43SWilliam A. Kennington III phosphor::network::refreshTimeout); 320bd649af9SWilliam A. Kennington III 3211bbe3d1eSWilliam A. Kennington III sd_event_loop(eventPtr.get()); 3221bbe3d1eSWilliam A. Kennington III } 323