Nirmal Agarwal
Nirmal Agarwal

Reputation: 205

Get pid of the process which has triggered some signal

Is it possible to find out the process id of the process which has caused some signal. In my scenario, I have multiple children of a process running, and I want to know which one of them sent the signal.

Upvotes: 9

Views: 5790

Answers (4)

Ivan Kolesnikov
Ivan Kolesnikov

Reputation: 1811

It's a non-blocking method to get a child PID which has triggered SIGCHLD signal in Unix:

import os
import signal


def sigchld_handler(_sig, _frame):
    try:
        child_pid, _ = os.waitpid(-1, os.WNOHANG)
    except OSError:
        return
    print(child_pid)


signal.signal(signal.SIGCHLD, sigchld_handler)

Upvotes: 0

Pietro Battiston
Pietro Battiston

Reputation: 8380

It is (finally!) very simple with python 3.

The following is tested with python 3.3.3:

#! /usr/bin/python3

import signal
import time, os

def callme(num, frame):
    pass

# register the callback:
signal.signal(signal.SIGUSR1, callme)

print("py: Hi, I'm %d, talk to me with 'kill -SIGUSR1 %d'"
      % (os.getpid(),os.getpid()))

# wait for signal info:
while True:
    siginfo = signal.sigwaitinfo({signal.SIGUSR1})
    print("py: got %d from %d by user %d\n" % (siginfo.si_signo,
                                             siginfo.si_pid,
                                             siginfo.si_uid))

Upvotes: 9

kratenko
kratenko

Reputation: 7592

POSIX Linux does provide this information, check man sigaction(2): http://linux.die.net/man/2/sigaction

In C, I managed to get it running easily:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

static void my_handler(int signum, siginfo_t *siginfo, void *context) {
    printf("Got signal '%d' from process '%d' of user '%d'\n",
        signum, siginfo->si_pid, siginfo->si_uid);
}

int main(void) {
    struct sigaction act;
    memset(&act, '\0', sizeof(act));
    act.sa_sigaction = &my_handler;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1, &act, NULL);
    printf("Hi, my pid is %d\ntalk to me with 'kill -SIGUSR1 %d'\n", getpid(), getpid());
    while(1)
        sleep(1000);
    return 0;
}

Works pretty well with my 3.1.6 vanilla kernel and gcc 4.4.5 -- but I could not find any support for it in python.

So i started to try and build something on my own (but since I never did C/Python-Interaction before, it's probably somehow twisted up...)

I'm more or less keeping close to the example at http://docs.python.org/extending/extending.html and building the module according to http://docs.python.org/extending/building.html#building

sigpidmodule.c

#include <Python.h>
#include <signal.h>

static PyObject *callback = NULL;

static void direct_handler(int signum, siginfo_t *siginfo, void *context) {
    int pid = (int) siginfo->si_pid;
    printf("c: Signal reached c handler: signum=%d, pid=%d, handler=%p\n", 
        signum, pid, callback);
    if ( callback != NULL ) {
        PyObject *arglist = Py_BuildValue("(i,i)", signum, pid);
        printf("c: calling python callback\n");
        PyObject *result = PyObject_CallObject(callback, arglist);
        // decrease reference counter
        Py_DECREF(arglist);
        Py_DECREF(result);
    }
}

static PyObject *sigpid_register(PyObject *self, PyObject *args) {
    PyObject *result = NULL;
    PyObject *temp;
    if ( PyArg_ParseTuple(args, "O:set_callback", &temp) ) {
        if ( !PyCallable_Check(temp) ) {
            PyErr_SetString(PyExc_TypeError, "parameter must be callable");
            return NULL;
        }
    }
    Py_XINCREF(temp);     // inc refcount on new callback
    Py_XDECREF(callback); // dec refcount on old callback
    callback = temp;      // replace old callback with new
    printf("c: callback now: %p\n", (void *) callback);
    // return None
    Py_RETURN_NONE;
}

static PyObject *sigpid_ping(PyObject *self, PyObject *args) {
    if ( callback != NULL ) {
        PyObject *arglist = Py_BuildValue("(i,i)", 42, 23);
        printf("c: calling callback...\n");
        PyObject *result = PyObject_CallObject(callback, arglist);
        // decrease ref counters
        Py_DECREF(arglist);
        Py_DECREF(result);
    }
    // return None:
    Py_RETURN_NONE;
}

static PyMethodDef SigPidMethods[] = {
    {"register", sigpid_register, METH_VARARGS, "Register callback for SIGUSR1"},
    {"ping", sigpid_ping, METH_VARARGS, "Test if callback is working"},
    {NULL, NULL, 0, NULL},
};

PyMODINIT_FUNC initsigpid(void) {
    // initialize module:
    (void) Py_InitModule("sigpid", SigPidMethods);
    // set sighandler:
    struct sigaction act;
    memset(&act, '\0', sizeof(act));
    act.sa_sigaction = &direct_handler;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1, &act, NULL);
}

setup.py for building the module:

from distutils.core import setup, Extension

module1 = Extension('sigpid', sources= ['sigpidmodule.c'])

setup (name='SigPid', version='1.0', 
    description='SigPidingStuff', 
    ext_modules = [module1])

building the module with

python setup.py build

So, what's still missing is the python script using the module: test.py

import sigpid
import time, os

def callme(num, pid):
    '''
    Callback function to be called from c module
    '''
    print "py: got %d from %d\n" % (num, pid)

# register the callback:
sigpid.register(callme)

print "py: Hi, I'm %d, talk to me with 'kill -SIGUSR1 %d'" %(os.getpid(),os.getpid())
# wait for signal while doing nothing:
while True:
    time.sleep(1)

Everything works very well... up to:

python test.py

or as I have to actually call it to get the lib right:

PYTHONPATH=build/lib.linux-i686-2.6 python test.py

output:

c: callback now: 0xb744f534
py: Hi, I'm 2255, talk to me with 'kill -SIGUSR1 2255'
(from other term: kill -SIGUSR1 2255)
c: Signal reached c handler: signum=10, pid=2948, handler=0xb744f534
c: calling python callback
Segmentation fault

I don't know why I get this segfault, and I'm running out of ideas to fix it. I guess it must have something to do with how c and python interact (I can think of some reasons, but it's all only guessing). Maybe someone with more experience in c-python-interaction can help here (or at least explain, what's the problem exactly). We might have a way to solve the problem there, at least on linux.

Upvotes: 6

Rafał Dowgird
Rafał Dowgird

Reputation: 45091

I believe it is not possible - the OS just does not pass this information to the target process.

Upvotes: -4

Related Questions