From 257b5762215fde31919b883441a1cae66db2606a Mon Sep 17 00:00:00 2001 From: jimmylomro Date: Tue, 15 Mar 2022 21:35:11 +0000 Subject: [PATCH] add python bindings --- .gitignore | 2 ++ pyproject.toml | 8 ++++++ setup.py | 62 +++++++++++++++++++++++++++++++++++++++++++ src/bindings.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ src/process.cpp | 3 +-- 5 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 pyproject.toml create mode 100644 setup.py create mode 100644 src/bindings.cpp diff --git a/.gitignore b/.gitignore index 517c40f..b2a98ec 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ nethogs.project libnethogs.so.* libnethogs.a results/ +build/ +nethogs.egg-info diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bc40523 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,8 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel", + "pybind11>=2.8.0", +] + +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0ed8487 --- /dev/null +++ b/setup.py @@ -0,0 +1,62 @@ +import glob +import subprocess +import sys + +from pybind11 import get_cmake_dir +# Available at setup time due to pyproject.toml +from pybind11.setup_helpers import Pybind11Extension, build_ext +from setuptools import setup + +_version_info = subprocess.run(['bash', "./determineVersion.sh"], stdout=subprocess.PIPE) +__version__ = _version_info.stdout.decode("utf-8").rstrip("\n").split("-")[0] if _version_info else "0.0.0" + +OBJS = [ + "src/bindings.cpp", + "src/libnethogs.cpp", + "src/packet.cpp", + "src/connection.cpp", + "src/process.cpp", + "src/decpcap.c", + "src/inode2prog.cpp", + "src/conninode.cpp", + "src/devices.cpp" +] + +FLAGS = [ + "-Wall", + "-Wextra", + "-Wno-missing-field-initializers", + "--std=c++0x", + "-O3", + "-fPIC", + '-DVERSION="{}"'.format(__version__) +] +# The main interface is through Pybind11Extension. +# * You can add cxx_std=11/14/17, and then build_ext can be removed. +# * You can set include_pybind11=false to add the include directory yourself, +# say from a submodule. +# +# Note: +# Sort input source files if you glob sources to ensure bit-for-bit +# reproducible builds (https://github.com/pybind/python_example/pull/53) + +ext_modules = [ + Pybind11Extension( + "nethogs", + sources = OBJS, + extra_compile_args = FLAGS, + libraries = ["pcap"] + ), +] + +setup( + name="nethogs", + version=__version__, + author="raboof", + url="https://github.com/raboof/nethogs", + description="Nethogs python bindings", + ext_modules=ext_modules, + cmdclass={"build_ext": build_ext}, + zip_safe=False, + python_requires=">=3.6", +) diff --git a/src/bindings.cpp b/src/bindings.cpp new file mode 100644 index 0000000..8c20ca6 --- /dev/null +++ b/src/bindings.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include "libnethogs.h" + +namespace py = pybind11; + +//--- for some reason this is a global defined in main.cpp +std::set pidsToWatch; + +//--- hacky way to get callbacks working and handle signals +std::function empty_callback; +std::function loop_callback; +void loop_callback_wrapper(int arg1, NethogsMonitorRecord const *arg2){ + py::gil_scoped_acquire acquire; + if (PyErr_CheckSignals() != 0) { + nethogsmonitor_breakloop(); + PyErr_Clear(); + } + else if (loop_callback) { + loop_callback(arg1, arg2); + } +} + +int nethogsmonitor_loop_py( + std::function &cb, + char *filter, + int to_ms) +{ + int retval; + loop_callback = cb; + { + py::gil_scoped_release release; + retval = nethogsmonitor_loop(loop_callback_wrapper, filter, to_ms); + } + loop_callback = empty_callback; + return retval; +} + +//--- python module binding +PYBIND11_MODULE(nethogs, m) { + py::class_(m, "NethogsMonitorRecord") + .def_readwrite("record_id", &NethogsMonitorRecord::record_id) + .def_readwrite("name", &NethogsMonitorRecord::name) + .def_readwrite("pid", &NethogsMonitorRecord::pid) + .def_readwrite("uid", &NethogsMonitorRecord::uid) + .def_readwrite("device_name", &NethogsMonitorRecord::device_name) + .def_readwrite("sent_bytes", &NethogsMonitorRecord::sent_bytes) + .def_readwrite("recv_bytes", &NethogsMonitorRecord::recv_bytes) + .def_readwrite("sent_kbs", &NethogsMonitorRecord::sent_kbs) + .def_readwrite("recv_kbs", &NethogsMonitorRecord::recv_kbs); + + m.def("nethogsmonitor_loop", &nethogsmonitor_loop_py, R"pbdoc( + Nethogs monitor loop + )pbdoc"); + m.def("nethogsmonitor_breakloop", &nethogsmonitor_breakloop, R"pbdoc( + Nethogs monitor loop break + )pbdoc"); + +#ifdef VERSION + m.attr("__version__") = VERSION; +#else + m.attr("__version__") = "unknown"; +#endif + +} diff --git a/src/process.cpp b/src/process.cpp index fcfb1f9..321f1f7 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -268,7 +268,7 @@ Process *getProcess(unsigned long inode, const char *devicename) { if (proc != NULL) return proc; - if ( !(pidsToWatch.empty()) && pidsToWatch.find(node->pid) == pidsToWatch.end() ) { + if ( !(pidsToWatch.empty()) && pidsToWatch.find(node->pid) == pidsToWatch.end() ) { return NULL; } @@ -440,4 +440,3 @@ void remove_timed_out_processes() { } void garbage_collect_processes() { garbage_collect_inodeproc(); } -