Recently I wanted to call some C code from Python to check who was sending a particular signal. Apparently this functionality is only available as of Python 3 so I decided to write a custom module.
Doing so is relatively easy but all the documentation I found only was usingdistutils to build the module and skipped over how to find the newly builtmodule. This post is mostly a write up of what I found out.
Compiling the Module
Compiling the Python module is easy. You just need to correct flags to pass tothe compiler. This information is easily accessible trough the python-config
utility.
Additionally you want to make a shared library. On macOS that means adding-dynamiclib
to the invocation.
clang `python-config --cflags` `python-config --ldflags` -dynamiclib mymodule.c -o mymodule.so
Make sure that the python-config
in your path matches the python
in yourpath. For example on macOS, if you have installed python using Homebrew, youmight have a different Python in /usr/bin
and /usr/local/bin
.
Loading the Module
The most straightforward approach is just having the shared library living inthe current directory. All you need is import mymodule
.
Python 2.7.10
Type "help", "copyright", "credits" or "license" for more information.
>>> import mymodule
Alternatively you can add the path where the modules lives to theDYLD_LIBRARY_PATH
or LD_LIBRARY_PATH
for macOS or Linux respectively, ormore specifically the PYTHONPATH
.
export DYLD_LIBRARY_PATH='/path/to/module':$DYLD_LIBRARY_PATH
Please note that the extension seems to matter. Giving the shared library theusual .dylib
extension on macOS did not work for me.
Python Module
Below is a listing with the code for the Python module.
#include <Python.h>
#include <libproc.h>
#include <signal.h>
static void sighandler_handle(int signum, siginfo_t *siginfo, void *context) {
int pid = getpid();
int pgrp = getpgrp();
int si_pid = (int)siginfo->si_pid;
char si_path[PROC_PIDPATHINFO_MAXSIZE];
// Print some info about the receiving and sending process.
fprintf(stderr, "Signal received:\n");
fprintf(stderr, "\tsignal: %d\n", signum);
fprintf(stderr, "\tpid: %d\n", pid);
fprintf(stderr, "\tpgrp: %d\n", pgrp);
fprintf(stderr, "\tfrom: %d\n", si_pid);
// Try obtaining the process name for the sending pid.
int ret = proc_pidpath(si_pid, si_path, sizeof(si_path));
if (ret <= 0) {
fprintf(stderr, "\tpath: %s\n", strerror(errno));
} else {
fprintf(stderr, "\tpath: %s\n", si_path);
}
exit(1);
}
static void sighandler_set(int signal) {
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_sigaction = &sighandler_handle;
sa.sa_flags = SA_SIGINFO;
sigaction(signal, &sa, NULL);
}
static PyObject *sighandler_register(PyObject *self, PyObject *args) {
int signal;
if (!PyArg_ParseTuple(args, "i", &signal))
return NULL;
sighandler_set(signal);
Py_RETURN_NONE;
}
static PyMethodDef sighandler_methods[] = {
{"register", sighandler_register, METH_VARARGS,
"Register signal handler by signal number"},
{NULL, NULL, 0, NULL},
};
PyMODINIT_FUNC initsighandler(void) {
PyObject *m = Py_InitModule("sighandler", sighandler_methods);
if (m == NULL)
return;
}