0

I have a script which is doing some stuff, e.g. automated testing. For an unknown reason it is getting (seamingly) randomly terminated by a SIGTERM signal - the goal is to try to identify who is sending the signal - the PID of the sender.

I've tried using the signal library, but it seems that it does not provide enough information about the sender, in case of using a handler setting like signal.signal(signal.SIGTERM, some_handler) - the handler is only passed the signal number (15 in the case), and current stack, which are not helpful.

I also tried using ctypes and creating a handler (based on answer to this), after some changes i managed to get the handler to accept siginfo_t - but the values for siginfo_t.contents.si_pid and siginfo_t.contents.si_code do not make sense to me, as they don't resemble the sender's PID, and the code is not 15.

The code I used is listed below:

#!/usr/bin/env python3

import sys
import os
import time
from ctypes import Structure, Union, POINTER, CFUNCTYPE, CDLL, byref, sizeof, \
    c_short, c_int, c_uint, c_long, c_ulong, c_char_p, c_void_p


SIGTERM = 15
SA_SIGINFO = 4

class sigset_t(Structure):
    _fields_ = [
        ("__val", c_ulong * (1024 // (8 * sizeof (c_long)))),
    ]


class sigval_t(Union):
    _fields_ = [
        ("sival_int", c_int),
        ("sival_ptr", c_void_p),
    ]


class siginfo_t(Structure):
    _fields_ = [
        ("si_signo", c_int),
        ("si_errno", c_int),
        ("si_code", c_int),
        ("_pad", c_int * 29),
        ("si_pid", c_uint)
    ]


sa_sigaction_functype = CFUNCTYPE(None, c_int, POINTER(siginfo_t), c_void_p)

sa_handler_functype = CFUNCTYPE(None, c_int, use_errno=True)


class SIGACTION(Structure):
    _fields_ = [
        # ("sa_handler", sa_handler_functype),
        ("sa_sigaction", sa_sigaction_functype),
        # ("sa_mask", sigset_t),
        ("sa_mask", siginfo_t),
        ("sa_flags", c_int),
        ("sa_restorer", c_void_p),
    ]


libc = CDLL(None)

def sighandler(sig, siginfo_t, something):  # Signal handler function
    libc.puts.argtypes = [c_char_p]
    libc.puts.restype = c_int
    libc.puts("Custom signal handler called for signal {0:d}".format(sig).encode())
    # print(str(siginfo_t.contents))
    print("Sender PID: " + str(siginfo_t.contents.si_pid))
    print("Signal Number: " + str(siginfo_t.contents.si_signo))


def main(*argv):

    libc.sigaction.argtypes = [c_int, POINTER(SIGACTION), POINTER(SIGACTION)]
    libc.sigaction.restype = c_int

    signal_number = SIGTERM
    act = SIGACTION(
                    # sa_handler=sa_handler_functype(sighandler),
                    sa_sigaction=sa_sigaction_functype(sighandler),
                    sa_flag=SA_SIGINFO)

    res = libc.sigaction(signal_number, byref(act), None)
    print("sigaction result: {0:d}".format(res))
    print("PId {0:d} waiting for SIG {1:d}...".format(os.getpid(), signal_number))

    while 1:
        time.sleep(0.1)


if __name__ == "__main__":
    main(*sys.argv[1:])
    print("\nDone.")

And the values I get when the handler is called (signal is sent by process with PID 14783):

siginfo_t.contents

si_code: 11340640
si_errno: 1
si_pid: 2
si_signo: 1

This is my first time using ctypes and C in general (hence, the code may not make too much sense), so any help with clarifying why do I get invalid values and how to fix it would be appreciated, thanks.

Python 3.8, tested on Ubuntu 18.04.5 LTS

  • Your structures don't agree with, for example, https://man7.org/linux/man-pages/man2/sigaction.2.html. `si_pid` is at byte offset 16 (assuming 32-bit int) on that man page but yours is at offset 128. – Mark Tolonen May 12 '21 at 17:40

1 Answers1

0

Check this post for debugging your issue - https://stackoverflow.com/a/21434576/7812715

Replace signal.SIGUSR1 with signal.SIGTERM everywhere in the code snippet given on the post and you should be able to catch the sender process id.