From 0cf0c3b428ed7be0238ff2ea62e64d14fab09675 Mon Sep 17 00:00:00 2001 From: Allan Liu Date: Tue, 28 Feb 2017 13:02:13 -0500 Subject: [PATCH] lldp_neighbors: posix table spec and table implementation (#2957) --- osquery/tables/CMakeLists.txt | 2 + .../networking/posix/lldp_neighbors.cpp | 417 ++++++++++++++++++ specs/posix/lldp_neighbors.table | 74 ++++ tools/provision.sh | 3 +- tools/provision/formula/lldpd.rb | 42 ++ 5 files changed, 537 insertions(+), 1 deletion(-) create mode 100644 osquery/tables/networking/posix/lldp_neighbors.cpp create mode 100644 specs/posix/lldp_neighbors.table create mode 100644 tools/provision/formula/lldpd.rb diff --git a/osquery/tables/CMakeLists.txt b/osquery/tables/CMakeLists.txt index 6042a73d..8e8b2bc5 100644 --- a/osquery/tables/CMakeLists.txt +++ b/osquery/tables/CMakeLists.txt @@ -92,6 +92,8 @@ endif() if(APPLE OR LINUX) file(GLOB OSQUERY_CROSS_EVENTS_TABLES "events/*.cpp") + ADD_OSQUERY_LINK_ADDITIONAL("lldpctl") + if(APPLE) ADD_OSQUERY_LINK_ADDITIONAL("libiconv") endif() diff --git a/osquery/tables/networking/posix/lldp_neighbors.cpp b/osquery/tables/networking/posix/lldp_neighbors.cpp new file mode 100644 index 00000000..6af0f37f --- /dev/null +++ b/osquery/tables/networking/posix/lldp_neighbors.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace osquery { +namespace tables { + +// Deleter to be used with unique_ptr wrap on lldp_atom_t pointers. +auto delLLDPAtom = [](lldpctl_atom_t* a) { lldpctl_atom_dec_ref(a); }; + +/* kNoAtomStrDefault is the default value used when lldp_atom_get_str does not + * return a valid string value. */ +const std::string kNoAtomStrDefault = "unknown"; + +struct ChassisCapability { + bool available; + bool enabled; +}; + +inline std::string getAtomStr(lldpctl_atom_t* atom, lldpctl_key_t key) { + if (atom == nullptr) { + return kNoAtomStrDefault; + } + + const char* val = lldpctl_atom_get_str(atom, key); + if (val == nullptr) { + return kNoAtomStrDefault; + } + + return val; +} + +inline std::string commaDelimitedStr(lldpctl_atom_t* things, + lldpctl_key_t key) { + if (things == nullptr) { + return ""; + } + + std::string result; + lldpctl_atom_t* each = nullptr; + + lldpctl_atom_foreach(things, each) { + result = result + getAtomStr(each, key) + ","; + } + // Remove trailing comma. + if (!result.empty()) { + result.pop_back(); + } + + return result; +} + +class LLDPNeighbor { + public: + /** + * @brief LLDPNeighbor is responsible for retrieving LLDP information on one + * neighbor. On construction, a Row is initialized for member functions to + * fill. + * + * It does not handle the the reference decrementing of the chassis and port + * parameter. That responsibility is on the caller. + * + * @param port liblldpctl atom type of given port + * @param chassis liblldpctl atom type of given chassis + * + */ + LLDPNeighbor(lldpctl_atom_t* port, lldpctl_atom_t* chassis) + : port_(port), chassis_(chassis) {} + + LLDPNeighbor(const LLDPNeighbor& ln) = delete; + + Row& getNeighbor(); + + private: + lldpctl_atom_t* port_; + lldpctl_atom_t* chassis_; + Row row_; + + void getChassis(); + void getChasisCapability(u_int8_t mask, + std::string const& availCol, + std::string const& enabledCol); + void getPort(); + void getPMDAutoNeg(long int autonegAdversised, + long int hdMask, + long int fdMask, + std::string const& hdCol, + std::string const& fdCol); + void getVLAN(); + void getMED(); + void getMEDCap(); + void getPIDs(); + void getPPVIDs(); +}; + +inline void LLDPNeighbor::getChasisCapability(u_int8_t mask, + std::string const& availCol, + std::string const& enabledCol) { + if (lldpctl_atom_get_int(chassis_, lldpctl_k_chassis_cap_available) & mask) { + row_[availCol] = "1"; + row_[enabledCol] = + (lldpctl_atom_get_int(chassis_, lldpctl_k_chassis_cap_enabled) & mask) + ? "1" + : "0"; + } +} + +inline void LLDPNeighbor::getPMDAutoNeg(long int autonegAdversised, + long int hdMask, + long int fdMask, + std::string const& hdCol, + std::string const& fdCol) { + row_[hdCol] = ((autonegAdversised & hdMask) ? "1" : "0"); + row_[fdCol] = ((autonegAdversised & fdMask) ? "1" : "0"); +} + +void LLDPNeighbor::getPIDs() { + std::unique_ptr pids( + lldpctl_atom_get(port_, lldpctl_k_port_pis), delLLDPAtom); + row_["pids"] = commaDelimitedStr(pids.get(), lldpctl_k_pi_id); +} + +void LLDPNeighbor::getPPVIDs() { + std::unique_ptr ppvids( + lldpctl_atom_get(port_, lldpctl_k_port_ppvids), delLLDPAtom); + if (ppvids.get() == nullptr) { + return; + } + + std::string supported; + std::string enabled; + + lldpctl_atom_t* ppvid = nullptr; + lldpctl_atom_foreach(ppvids.get(), ppvid) { + long int status = lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_status); + long int id = lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_id); + + if (status > 0 && id > 0) { + std::string name = getAtomStr(ppvid, lldpctl_k_ppvid_id); + + if (status & LLDP_PPVID_CAP_SUPPORTED) { + supported = supported + name + ","; + } + + if (status & LLDP_PPVID_CAP_ENABLED) { + enabled = enabled + name + ","; + } + } + } + + if (supported.size() > 0) { + supported.pop_back(); + } + row_["ppvids_supported"] = supported; + + if (enabled.size() > 0) { + enabled.pop_back(); + } + row_["ppvids_enabled"] = enabled; +} + +void LLDPNeighbor::getMEDCap() { + long int availableCap = + lldpctl_atom_get_int(chassis_, lldpctl_k_chassis_med_cap); + if (availableCap < 0) { + return; + } + + row_["med_capability_capabilities"] = + (availableCap & LLDP_MED_CAP_CAP) ? "1" : "0"; + row_["med_capability_policy"] = + (availableCap & LLDP_MED_CAP_POLICY) ? "1" : "0"; + row_["med_capability_location"] = + (availableCap & LLDP_MED_CAP_LOCATION) ? "1" : "0"; + row_["med_capability_mdi_pse"] = + (availableCap & LLDP_MED_CAP_MDI_PSE) ? "1" : "0"; + row_["med_capability_mdi_pd"] = + (availableCap & LLDP_MED_CAP_MDI_PD) ? "1" : "0"; + row_["med_capability_inventory"] = + (availableCap & LLDP_MED_CAP_IV) ? "1" : "0"; +} + +void LLDPNeighbor::getMED() { + if (lldpctl_atom_get_int(chassis_, lldpctl_k_chassis_med_type) <= 0) { + return; + } + + row_["med_device_type"] = getAtomStr(chassis_, lldpctl_k_chassis_med_type); + // Capabilities status + getMEDCap(); + + // Policy Capabilities + std::unique_ptr policies( + lldpctl_atom_get(port_, lldpctl_k_port_med_policies), delLLDPAtom); + row_["med_policies"] = + commaDelimitedStr(policies.get(), lldpctl_k_med_policy_type); +} + +void LLDPNeighbor::getVLAN() { + row_["pvid"] = getAtomStr(port_, lldpctl_k_port_vlan_pvid); + + std::unique_ptr vlans( + lldpctl_atom_get(port_, lldpctl_k_port_vlans), delLLDPAtom); + + row_["vlans"] = commaDelimitedStr(vlans.get(), lldpctl_k_vlan_id); +} + +void LLDPNeighbor::getPort() { + row_["port_id"] = getAtomStr(port_, lldpctl_k_port_id); + row_["port_id_type"] = getAtomStr(port_, lldpctl_k_port_id_subtype); + row_["port_aggregation_id"] = getAtomStr(port_, lldpctl_k_port_dot3_aggregid); + row_["port_description"] = getAtomStr(port_, lldpctl_k_port_descr); + row_["port_ttl"] = getAtomStr(port_, lldpctl_k_port_age); + row_["port_mfs"] = getAtomStr(port_, lldpctl_k_port_dot3_mfs); + + // Dot3 power stuff + std::unique_ptr power( + lldpctl_atom_get(port_, lldpctl_k_port_dot3_power), delLLDPAtom); + auto pp = power.get(); + if (pp != nullptr && + lldpctl_atom_get_int(pp, lldpctl_k_dot3_power_devicetype) > 0) { + row_["power_device_type"] = getAtomStr(pp, lldpctl_k_dot3_power_devicetype); + row_["power_mdi_supported"] = + lldpctl_atom_get_int(pp, lldpctl_k_dot3_power_supported); + row_["power_mdi_enabled"] = + lldpctl_atom_get_int(pp, lldpctl_k_dot3_power_enabled); + row_["power_paircontrol_enabled"] = + lldpctl_atom_get_int(pp, lldpctl_k_dot3_power_paircontrol); + row_["power_pairs"] = getAtomStr(pp, lldpctl_k_dot3_power_pairs); + row_["power_class"] = getAtomStr(pp, lldpctl_k_dot3_power_class); + row_["power_8023at_enabled"] = + (lldpctl_atom_get_int(pp, lldpctl_k_dot3_power_type) > + LLDP_DOT3_POWER_8023AT_OFF); + row_["power_8023at_power_type"] = getAtomStr(pp, lldpctl_k_dot3_power_type); + row_["power_8023at_power_source"] = + getAtomStr(pp, lldpctl_k_dot3_power_source); + row_["power_8023at_power_priority"] = + getAtomStr(pp, lldpctl_k_dot3_power_priority); + row_["power_8023at_power_requested"] = + getAtomStr(pp, lldpctl_k_dot3_power_requested); + row_["power_8023at_power_allocated"] = + getAtomStr(pp, lldpctl_k_dot3_power_allocated); + } + + // Auto-Negotiations + bool autoneg_supported = + lldpctl_atom_get_int(port_, lldpctl_k_port_dot3_autoneg_support); + row_["port_autoneg_supported"] = (autoneg_supported) ? "1" : "0"; + + bool autoneg_enabled = + lldpctl_atom_get_int(port_, lldpctl_k_port_dot3_autoneg_enabled); + row_["port_autoneg_enabled"] = (autoneg_enabled) ? "1" : "0"; + + row_["port_mau_type"] = getAtomStr(port_, lldpctl_k_port_dot3_mautype); + + if (autoneg_supported && autoneg_enabled) { + long int advertised = + lldpctl_atom_get_int(port_, lldpctl_k_port_dot3_autoneg_advertised); + + getPMDAutoNeg(advertised, + LLDP_DOT3_LINK_AUTONEG_10BASE_T, + LLDP_DOT3_LINK_AUTONEG_10BASET_FD, + "port_autoneg_10baset_hd_enabled", + "port_autoneg_10baset_fd_enabled"); + getPMDAutoNeg(advertised, + LLDP_DOT3_LINK_AUTONEG_100BASE_TX, + LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD, + "port_autoneg_100basetx_hd_enabled", + "port_autoneg_100basetx_fd_enabled"); + getPMDAutoNeg(advertised, + LLDP_DOT3_LINK_AUTONEG_100BASE_T2, + LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD, + "port_autoneg_100baset2_hd_enabled", + "port_autoneg_100baset2_fd_enabled"); + getPMDAutoNeg(advertised, + LLDP_DOT3_LINK_AUTONEG_100BASE_T4, + LLDP_DOT3_LINK_AUTONEG_100BASE_T4, + "port_autoneg_100baset4_hd_enabled", + "port_autoneg_100baset4_fd_enabled"); + getPMDAutoNeg(advertised, + LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD, + "port_autoneg_1000basex_hd_enabled", + "port_autoneg_1000basex_fd_enabled"); + getPMDAutoNeg(advertised, + LLDP_DOT3_LINK_AUTONEG_1000BASE_T, + LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD, + "port_autoneg_1000baset_hd_enabled", + "port_autoneg_1000baset_fd_enabled"); + } +} + +void LLDPNeighbor::getChassis() { + row_["rid"] = getAtomStr(chassis_, lldpctl_k_chassis_index); + row_["chassis_id"] = getAtomStr(chassis_, lldpctl_k_chassis_id); + row_["chassis_id_type"] = getAtomStr(chassis_, lldpctl_k_chassis_id_subtype); + row_["chassis_sysname"] = getAtomStr(chassis_, lldpctl_k_chassis_name); + row_["chassis_sys_description"] = + getAtomStr(chassis_, lldpctl_k_chassis_descr); + + getChasisCapability(LLDP_CAP_BRIDGE, + "chassis_bridge_capability_available", + "chassis_bridge_capability_enabled"); + getChasisCapability(LLDP_CAP_ROUTER, + "chassis_router_capability_available", + "chassis_router_capability_enabled"); + getChasisCapability(LLDP_CAP_WLAN, + "chassis_wlan_capability_available", + "chassis_wlan_capability_enabled"); + getChasisCapability(LLDP_CAP_REPEATER, + "chassis_repeater_capability_available", + "chassis_repeater_capability_enabled"); + getChasisCapability(LLDP_CAP_TELEPHONE, + "chassis_tel_capability_available", + "chassis_tel_capability_enabled"); + getChasisCapability(LLDP_CAP_DOCSIS, + "chassis_docsis_capability_available", + "chassis_docsis_capability_enabled"); + getChasisCapability(LLDP_CAP_STATION, + "chassis_station_capability_available", + "chassis_station_capability_enabled"); + getChasisCapability(LLDP_CAP_OTHER, + "chassis_other_capability_available", + "chassis_other_capability_enabled"); + + std::unique_ptr mgmts( + lldpctl_atom_get(chassis_, lldpctl_k_chassis_mgmt), delLLDPAtom); + row_["chassis_mgmt_ips"] = commaDelimitedStr(mgmts.get(), lldpctl_k_mgmt_ip); +} + +/** + * @brief getNeighbor retreives all LLDP information of the given neighbor port + * and chassis. + * + * @return filled osquery::Row of lldp information for a given interface + * + */ +Row& LLDPNeighbor::getNeighbor() { + getChassis(); + getPort(); + getVLAN(); + getMED(); + getMEDCap(); + getPIDs(); + getPPVIDs(); + + return row_; +} + +QueryData genLLDPNeighbors(QueryContext& context) { + QueryData rows; + + auto delConn = [](lldpctl_conn_t* c) { lldpctl_release(c); }; + std::unique_ptr conn( + lldpctl_new(nullptr, nullptr, nullptr), delConn); + + lldpctl_error_t err = lldpctl_last_error(conn.get()); + if (err != LLDPCTL_NO_ERROR) { + LOG(ERROR) << "could not initiate new lldpd connection: " + << lldpctl_strerror(err); + return rows; + } + + std::unique_ptr interfaces( + lldpctl_get_interfaces(conn.get()), delLLDPAtom); + + err = lldpctl_last_error(conn.get()); + if (err != LLDPCTL_NO_ERROR) { + LOG(WARNING) << "could not connect to lldpd (hint: you might need to " + "install lldpd v0.9.X or run in sudo): " + << lldpctl_strerror(err); + return rows; + } + + lldpctl_atom_t* interface = nullptr; + lldpctl_atom_foreach(interfaces.get(), interface) { + std::string ifaceName = getAtomStr(interface, lldpctl_k_interface_name); + + std::unique_ptr port( + lldpctl_get_port(interface), delLLDPAtom); + std::unique_ptr neighbors( + lldpctl_atom_get(port.get(), lldpctl_k_port_neighbors), delLLDPAtom); + + lldpctl_atom_t* neighbor = nullptr; + lldpctl_atom_foreach(neighbors.get(), neighbor) { + std::unique_ptr chassis( + lldpctl_atom_get(neighbor, lldpctl_k_port_chassis), delLLDPAtom); + + LLDPNeighbor n(neighbor, chassis.get()); + Row& row = n.getNeighbor(); + row["interface"] = ifaceName; + rows.push_back(row); + } + } + + return rows; +} +} +} diff --git a/specs/posix/lldp_neighbors.table b/specs/posix/lldp_neighbors.table new file mode 100644 index 00000000..50088b69 --- /dev/null +++ b/specs/posix/lldp_neighbors.table @@ -0,0 +1,74 @@ +table_name("lldp_neighbors") +description("LLDP neighbors of interfaces.") +schema([ + Column("interface", TEXT, "Interface name"), + Column("rid", INTEGER, "Neighbor chassis index"), + Column("chassis_id_type", TEXT, "Neighbor chassis ID type"), + Column("chassis_id", TEXT, "Neighbor chassis ID value"), + Column("chassis_sysname", TEXT, "CPU brand string, contains vendor and model"), + Column("chassis_sys_description", INTEGER, "Max number of CPU physical cores"), + Column("chassis_bridge_capability_available", INTEGER, "Chassis bridge capability availability"), + Column("chassis_bridge_capability_enabled", INTEGER, "Is chassis bridge capability enabled."), + Column("chassis_router_capability_available", INTEGER, "Chassis router capability availability"), + Column("chassis_router_capability_enabled", INTEGER, "Chassis router capability enabled"), + Column("chassis_repeater_capability_available", INTEGER, "Chassis repeater capability availability"), + Column("chassis_repeater_capability_enabled", INTEGER, "Chassis repeater capability enabled"), + Column("chassis_wlan_capability_available", INTEGER, "Chassis wlan capability availability"), + Column("chassis_wlan_capability_enabled", INTEGER, "Chassis wlan capability enabled"), + Column("chassis_tel_capability_available", INTEGER, "Chassis telephone capability availability"), + Column("chassis_tel_capability_enabled", INTEGER, "Chassis telephone capability enabled"), + Column("chassis_docsis_capability_available", INTEGER, "Chassis DOCSIS capability availability"), + Column("chassis_docsis_capability_enabled", INTEGER, "Chassis DOCSIS capability enabled"), + Column("chassis_station_capability_available", INTEGER, "Chassis station capability availability"), + Column("chassis_station_capability_enabled", INTEGER, "Chassis station capability enabled"), + Column("chassis_other_capability_available", INTEGER, "Chassis other capability availability"), + Column("chassis_other_capability_enabled", INTEGER, "Chassis other capability enabled"), + Column("chassis_mgmt_ips", TEXT, "Comma delimited list of chassis management IPS"), + Column("port_id_type", TEXT, "Port ID type"), + Column("port_id", TEXT, "Port ID value"), + Column("port_description", TEXT, "Port description"), + Column("port_ttl", BIGINT, "Age of neighbor port"), + Column("port_mfs", BIGINT, "Port max frame size"), + Column("port_aggregation_id", TEXT, "Port aggregation ID"), + Column("port_autoneg_supported", INTEGER, "Auto negotiation supported"), + Column("port_autoneg_enabled", INTEGER, "Is auto negotiation enabled"), + Column("port_mau_type", TEXT, "MAU type"), + Column("port_autoneg_10baset_hd_enabled", INTEGER, "10Base-T HD auto negotiation enabled"), + Column("port_autoneg_10baset_fd_enabled", INTEGER, "10Base-T FD auto negotiation enabled"), + Column("port_autoneg_100basetx_hd_enabled", INTEGER, "100Base-TX HD auto negotiation enabled"), + Column("port_autoneg_100basetx_fd_enabled", INTEGER, "100Base-TX FD auto negotiation enabled"), + Column("port_autoneg_100baset2_hd_enabled", INTEGER, "100Base-T2 HD auto negotiation enabled"), + Column("port_autoneg_100baset2_fd_enabled", INTEGER, "100Base-T2 FD auto negotiation enabled"), + Column("port_autoneg_100baset4_hd_enabled", INTEGER, "100Base-T4 HD auto negotiation enabled"), + Column("port_autoneg_100baset4_fd_enabled", INTEGER, "100Base-T4 FD auto negotiation enabled"), + Column("port_autoneg_1000basex_hd_enabled", INTEGER, "1000Base-X HD auto negotiation enabled"), + Column("port_autoneg_1000basex_fd_enabled", INTEGER, "1000Base-X FD auto negotiation enabled"), + Column("port_autoneg_1000baset_hd_enabled", INTEGER, "1000Base-T HD auto negotiation enabled"), + Column("port_autoneg_1000baset_fd_enabled", INTEGER, "1000Base-T FD auto negotiation enabled"), + Column("power_device_type", TEXT, "Dot3 power device type"), + Column("power_mdi_supported", INTEGER, "MDI power supported"), + Column("power_mdi_enabled", INTEGER, "Is MDI power enabled"), + Column("power_paircontrol_enabled", INTEGER, "Is power pair control enabled"), + Column("power_pairs", TEXT, "Dot3 power pairs"), + Column("power_class", TEXT, "Power class"), + Column("power_8023at_enabled", INTEGER, "Is 802.3at enabled"), + Column("power_8023at_power_type", TEXT, "802.3at power type"), + Column("power_8023at_power_source", TEXT, "802.3at power source"), + Column("power_8023at_power_priority", TEXT, "802.3at power priority"), + Column("power_8023at_power_allocated", TEXT, "802.3at power allocated"), + Column("power_8023at_power_requested", TEXT, "802.3at power requested"), + Column("med_device_type", TEXT, "Chassis MED type"), + Column("med_capability_capabilities", INTEGER, "Is MED capabilities enabled"), + Column("med_capability_policy", INTEGER, "Is MED policy capability enabled"), + Column("med_capability_location", INTEGER, "Is MED location capability enabled"), + Column("med_capability_mdi_pse", INTEGER, "Is MED MDI PSE capability enabled"), + Column("med_capability_mdi_pd", INTEGER, "Is MED MDI PD capability enabled"), + Column("med_capability_inventory", INTEGER, "Is MED inventory capability enabled"), + Column("med_policies", TEXT, "Comma delimited list of MED policies"), + Column("vlans", TEXT, "Comma delimited list of vlan ids"), + Column("pvid", TEXT, "Primary VLAN id"), + Column("ppvids_supported", TEXT, "Comma delimited list of supported PPVIDs"), + Column("ppvids_enabled", TEXT, "Comma delimited list of enabled PPVIDs"), + Column("pids", TEXT, "Comma delimited list of PIDs"), +]) +implementation("networking/lldp_neighbors@genLLDPNeighbors") diff --git a/tools/provision.sh b/tools/provision.sh index 1a3492c2..720269b2 100755 --- a/tools/provision.sh +++ b/tools/provision.sh @@ -100,7 +100,7 @@ function platform_linux_main() { brew_dependency osquery/osquery-local/libaudit brew_dependency osquery/osquery-local/libdpkg brew_dependency osquery/osquery-local/librpm - + brew_dependency osquery/osquery-local/lldpd } function platform_darwin_main() { @@ -114,6 +114,7 @@ function platform_darwin_main() { brew_tool autoconf brew_tool automake brew_tool libtool + brew_tool lldpd brew_dependency osquery/osquery-local/libxml2 brew_dependency osquery/osquery-local/openssl diff --git a/tools/provision/formula/lldpd.rb b/tools/provision/formula/lldpd.rb new file mode 100644 index 00000000..c122fd3d --- /dev/null +++ b/tools/provision/formula/lldpd.rb @@ -0,0 +1,42 @@ +require File.expand_path("../Abstract/abstract-osquery-formula", __FILE__) + +class Lldpd < AbstractOsqueryFormula + desc "lldpd is an implmentation of LLDP(802.1ab)" + homepage "https://vincentbernat.github.io/lldpd" + url "https://media.luffy.cx/files/lldpd/lldpd-0.9.6.tar.gz" + sha256 "e74e2dd7e2a233ca1ff385c925ddae2a916d302819d1433741407d2f8fb0ddd8" + + option :universal + + depends_on "pkg-config" => :build + depends_on "libevent" + + def install + if OS.mac? + args = [ + "--prefix=#{prefix}", + "--sysconfdir=#{etc}", + "--localstatedir=#{var}", + "--enable-shared=no", + "--with-privsep-chroot=/var/empty", + "--with-privsep-user=nobody", + "--with-privsep-group=nogroup", + "--with-launchddaemonsdir=no", + ] + else + args = [ + "--prefix=#{prefix}", + "--sysconfdir=#{etc}", + "--localstatedir=/var", + "--enable-shared=no", + "--with-privsep-chroot=/var/empty", + "--with-privsep-user=nobody", + "--with-privsep-group=nogroup", + ] + end + + system "./configure", *args + system "make" + system "make", "install" + end +end