5

I have tried set signal handler with sigaction and ctypes. (I know it is able to do with signal module in python, but I want to try it for learn.)

When I sent SIGTERM to this process, but it does not call handler that I set, only print "Terminated". Why it does not invoke the handler?

I using Ubuntu 19.10 and Python 3.7.5 x64.

import ctypes
from ctypes import *
from ctypes.util import *
from os import getpid


class sigset_t(Structure):
    __fields__ = [
        ("__val",               c_ulong*16)        
    ]

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),
        ("si_trapno",           c_int),
        ("si_pid",              c_uint),
        ("si_status",           c_int),
        ("si_utime",            c_long),
        ("si_stime",            c_long),
        ("si_value",            sigval_t),
        ("si_int",              c_int),
        ("si_ptr",              c_void_p),
        ("si_overrun",          c_int),
        ("si_timerid",          c_int),
        ("si_addr",             c_void_p),
        ("si_band",             c_long),
        ("si_fd",               c_int),
        ("si_addr_lsb",         c_short),
        ("si_call_addr",        c_void_p),
        ("si_syscall",          c_int),
        ("si_arch",             c_uint)

    ]

sa_handler_functype = CFUNCTYPE(None, c_int)
sigaction_functype = CFUNCTYPE(None, c_int, siginfo_t, c_void_p)


class SIGACTION(Structure):
    __fields__ = [
        ("sa_handler",          sa_handler_functype),
        ("sa_sigaction",        sigaction_functype),
        ("sa_mask",             sigset_t),
        ("sa_flags",            c_int),

    ]


def p(sig): # signal handler function
    libc.puts("bye")

libc_path = find_library("c")
libc = CDLL(libc_path)

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

act = SIGACTION(sa_handler=sa_handler_functype(p))
print(act)
res = libc.sigaction(15, act, None)
print(res)
print("pid:", getpid()) # show pid
while True: # wait until receive sigterm signal
    pass
KiYugadgeter
  • 3,796
  • 7
  • 34
  • 74

1 Answers1

2

Listing [Python 3.Docs]: ctypes - A foreign function library for Python.

There are some problems with the code:

  • ctypes.Structure descendants need to define _fields_ (and not __fields__). Changed the structures accordingly
  • Passed a struct sigaction * (pointer) as argument to sigaction
  • Modified structures definitions based on contents from files in /usr/include/bits (on my Ubtu 16 x64 VM)
  • Defined argtypes and restype for any used function (puts). Check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for more details
  • Other (minor) changes

code00.py:

#!/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


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),
    ]


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_mask", sigset_t),
        ("sa_flags", c_int),
        ("sa_restorer", c_void_p),
    ]


libc = CDLL(None)


def sighandler(sig):  # 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())


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))
    #print(act)

    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__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(*sys.argv[1:])
    print("\nDone.")

Output (while program running, sent SIGTERM twice then SIGKILL from another terminal):

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q059521251]> uname -m
x86_64
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q059521251]> cat /etc/lsb-release | grep DESCR
DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q059521251]> python3 code00.py
Python 3.5.2 (default, Oct  8 2019, 13:06:37) [GCC 5.4.0 20160609] 64bit on linux

sigaction result: 0
PId 20050 waiting for SIG 15...
Custom signal handler called for signal 15
Custom signal handler called for signal 15
Killed
CristiFati
  • 38,250
  • 9
  • 50
  • 87