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