79

Can I provide/pass any arguments to signal handler?

/* Signal handling */
struct sigaction act;
act.sa_handler = signal_handler;
/* some more settings */

Now, handler looks like this:

void signal_handler(int signo) {
    /* some code */
}

If I want to do something special i.e. delete temp files, can I provide those files as an argument to this handler?

Edit 0: Thanks for the answers. We generally avoid/discourage use of global variables. And in this case, If you have a huge program, things can go wrong at different places and you might need to do a lot of cleanup. Why was the API designed this way?

Mat
  • 202,337
  • 40
  • 393
  • 406
hari
  • 9,439
  • 27
  • 76
  • 110

8 Answers8

71

You can't have data of your own passed to the signal handler as parameters. Instead you'll have to store your parameters in global variables. (And be really, really careful if you ever need to change those data after installing the the signal handler).

Response to edit 0: Historical reasons. Signals are a really old and really low-level design. Basically you're just given the kernel a single address to some machine code and asking it to go to this specific address if such and such happens. We're back in the "portable assembler" mindset here, where the kernels provide a no-frills baseline service, and whatever the user process can reasonably be expected to to for itself, it must do itself.

Also, the usual arguments against global variables don't really apply here. The signal handler itself is a global setting, so there is no relevant possibility of having several different sets of user-specified parameters for it around. (Well, actually it is not entirely global but only thread-global. But the threading API will include some mechanism for thread-local storage, which is just what you need in this case).

hmakholm left over Monica
  • 23,074
  • 3
  • 51
  • 73
  • 3
    Signal handlers are not per-thread. Changing the handler for a signal applies to all threads. – R.. GitHub STOP HELPING ICE Aug 07 '11 at 02:51
  • Right. But it still makes sense to want the signal handler to react to which thread it is handling for, which is what TLS can achieve. – hmakholm left over Monica Aug 07 '11 at 02:56
  • 3
    Actually it's unclear to me whether accessing TLS from a signal handler is valid. With glibc/NPTL it's almost surely *not valid* in general, because a signal could be delivered immediately after the thread is created but before control is passed to the start function, while the thread's copy of TLS is still being initialized. If you're careful to have signals blocked at this time, it may be okay, but it's a dubious practice still... – R.. GitHub STOP HELPING ICE Aug 07 '11 at 02:59
  • By the way, even without TLS, there's one way to identify which thread the signal handler is running from in an async-signal-safe way: `&errno` is unique for each thread! However, I believe this may not be safe in glibc/NPTL for the same reason I just described. If it's not, I'm pretty sure that's a conformance bug, but it's probably a sufficiently rare race to go undetected... – R.. GitHub STOP HELPING ICE Aug 07 '11 at 04:00
  • @R.. Signal handlers can be on a per thread basis. POSIX provides functionality that you can use to send signals to individual threads within the same process. – Curious Jan 05 '16 at 12:47
  • 1
    @Curious: Your two sentences are unrelated. Yes you can send a signal to a particular thread but the handler is global state. There is no way to set different handlers on a per-thread basis short of implementing your own dispatcher as the main signal handler, and this is both (1) difficult to coordinate between different libraries that may need to set a per-thread handler, and (2) technically impossible without UB because there's no AS-safe way for a signal handler to determine which thread it's running in. – R.. GitHub STOP HELPING ICE Jan 05 '16 at 17:58
  • What's all the "Edit 0" stuff in this question and several answers? Was there a user called "Edit 0" whose comments have been deleted, or who has changed their user name? In many places there is half of a useful-sounding dialog marked "Edit 0" and I'd really like to read the other half... – jez Dec 19 '20 at 01:43
19

A signal handler registration is already a global state equivalent to global variables. So it's no greater offense to use global variables to pass arguments to it. However, it's a huge mistake (almost certainly undefined behavior unless you're an expert!) to do anything from a signal handler anyway. If you instead just block signals and poll for them from your main program loop, you can avoid all these issues.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Thanks for the response. I am not entirely sure what you are suggesting here. Can you please elaborate? My concern is, lets say I have 5 functions in a program and something wrong can happen in any of them, how can I avoid having them declare `global` variables to take care of clean up? – hari Aug 07 '11 at 03:11
  • 5
    Unless you're an expert with signals, it's very dangerous to do anything from a signal handler. Instead you could just use the signal handler to save a flag that the signal happened, and check for it from various points outside the signal handler, or you could block signals and use `sigpending` or `sigwait` to poll for signals. – R.. GitHub STOP HELPING ICE Aug 07 '11 at 03:57
  • 2
    Wow, this is completely new for me. I've seen code doing clean up like temp files deletion or child process clean up before the main program terminate. Also when you say check for flags, how can that work if you get SIGTERM? Doesn't your program terminate right away if you do not handle that in your handler? Why is it `dangerous` to do things inside a handler? – hari Aug 07 '11 at 05:40
  • 5
    A signal handler can interrupt the program at any point. This means, unless you've taken care to ensure this can't happen, it could run with various data objects it wants to work with in inconsistent state. For instance the buffer pointers inside a `FILE` might be only partially updated, or the linked list of open `FILE`s might have a halfway-inserted or halfway-removed item partly linked to it. Or the internal `malloc` data structures might have halfway-freed memory referenced... If the signal handler calls functions that depend on that state being consistent, very bad things will happen! – R.. GitHub STOP HELPING ICE Aug 07 '11 at 05:49
  • POSIX addresses these issues by defining a set of functions which are "async-signal-safe", meaning they can be called from signal handlers without any worries. Any other function, which is "async-signal-unsafe", is not legal to call from a signal handler unless you can be sure that the signal handler could not possibly have "interrupted" any other async-signal-unsafe function. – R.. GitHub STOP HELPING ICE Aug 07 '11 at 05:51
  • Thanks for the information and explanation. I also read a bit more about signal handlers. – hari Aug 07 '11 at 06:01
  • 5
    By the way, one classic favorite "safe" way to use signal handlers is to open a pipe to yourself, and store the file descriptors somewhere global. The signal handler can write to the pipe (this is safe) and you can wait for input from the pipe in your main `select`/`poll` loop along with other file descriptors you're waiting for input from. – R.. GitHub STOP HELPING ICE Aug 07 '11 at 06:03
  • Oh, okay. I am not sure if I understand what you want to convey, but thanks. I will look up on web and try to understand it. Appreciate your help. – hari Aug 07 '11 at 06:14
  • I just posted a follow-up question: http://stackoverflow.com/questions/6971201/kill-is-unsafe-respect-to-signals-any-alternative – hari Aug 07 '11 at 06:40
  • 2
    @R.. there is signalfd() on Linux which does exactly that. Still a good idea. Few people consider using pipes to communicate *within* a process. Makes communication between threads much more easier and explicit. – MauganRa Mar 02 '14 at 02:53
11

This is a really old question but I think I can show you a nice trick that would have answered your problem. No need to use sigqueue or whatever.

I also dislike the use of globals variables so I had to find a clever way, in my case, to send a void ptr (which you can later cast to whatever suits your need).

Actually you can do this :

signal(SIGWHATEVER, (void (*)(int))sighandler); // Yes it works ! Even with -Wall -Wextra -Werror using gcc

Then your sighandler would look like this :

int sighandler(const int signal, void *ptr) // Actually void can be replaced with anything you want , MAGIC !

You might ask : How to get the *ptr then ?

Here's how : At initialization

signal(SIGWHATEVER, (void (*)(int))sighandler)
sighandler(FAKE_SIGNAL, your_ptr);

In your sighandler func :

int sighandler(const int signal, void *ptr)
{
  static my_struct saved = NULL;

  if (saved == NULL)
     saved = ptr;
  if (signal == SIGNALWHATEVER)
     // DO YOUR STUFF OR FREE YOUR PTR
   return (0);
}
rak007
  • 973
  • 12
  • 26
  • 6
    Nice trick, but unfortunately it's undefined by the standard. See 6.3.2.3(8): `if a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined`. – eush77 May 26 '17 at 14:32
9

Absolutely. You can pass integers and pointers to signal handlers by using sigqueue() instead of the usual kill().

http://man7.org/linux/man-pages/man2/sigqueue.2.html

David Yeager
  • 596
  • 5
  • 9
1

I think you it's better to use SA_SIGINFO in sa_flags so the handler will get void signal_handler(int sig, siginfo_t *info, void *secret) in siginfo_t you can provide your params. Ty:HAPPY code

fad
  • 43
  • 1
  • 9
  • 2
    You setup some pointer or other data in a `sigval` union then pass this to `sigqueue()` to enqueue a realtime (user-defined) signal, **then** you get back whatever you specified in the signal handler via the `sigval` union in the `siginfo_t` parameter. You cannot specify the user-defined data for a none realtime signal though. Moreover, using `sigqueue()` has already been mentioned earlier... – Wad Nov 26 '20 at 22:00
1

Store the names of the files in a global variable and then access it from the handler. The signal handler callback will only be passed one argument: the ID for the actual signal that caused the problem (eg SIGINT, SIGTSTP)

Edit 0: "There must be a rock solid reason for not allowing arguments to the handler." <-- There is an interrupt vector (basically, a set of jump addresses to routines for each possible signal). Given the way that the interrupt is triggered, based on the interrupt vector, a particular function is called. Unfortunately, it's not clear where the memory associated with the variable will be called, and depending on the interrupt that memory may actually be corrupted. There is a way to get around it, but then you can't leverage the existing int 0x80 assembly instruction (which some systems still use)

Foo Bah
  • 25,660
  • 5
  • 55
  • 79
0

Despite the very old question the problem still exists today. Basically to close temporary files, terminate threads correctly, etc. for example, this logic could be used:

volatile sig_atomic_t sig_received = 0;

void sigterm_handler(int signum)
{
    printf("SIGTERM. PID: %d\n", getpid());
    sig_received = 1;
}
    void sigint_handler(int signum)
{
    fprintf(stderr, "SIGINT. PID: %d\n", getpid());
}

...

int main()
{
    struct sigaction action;
    action.sa_handler = sigterm_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    sigaction(SIGTERM, &action, NULL);

    action.sa_handler = sigint_handler;
    sigaction(SIGINT, &action, NULL);

    pthread_t writer_thread, reader_thread;
    struct master_argv writer_args, reader_args;

    buffer_init(&(writer_args.buffer));
    buffer_init(&(reader_args.buffer));

    writer_args.pipename = PIPE_CAPOSC;
    reader_args.pipename = PIPE_CAPOLET;

    if (pthread_create(&writer_thread, NULL, master, (void *)&writer_args) != 0)
    {
        exit(1);
    }

    if (pthread_create(&reader_thread, NULL, master, (void *)&reader_args) != 0)
    {
        exit(1);
    }

    while (!sig_received)
    {
        sleep(1);
    }

    pthread_join(writer_thread, NULL);
    pthread_join(reader_thread, NULL);

    buffer_destroy(&(writer_args.buffer));
    buffer_destroy(&(reader_args.buffer));

    return 0;
}

Basically, within the signal manager, a sig_atomic_t flag is set which guarantees atomic access to this variable and volatile to signal to the compiler that this variable must not be optimized since it could undergo unexpected changes such as the modification by a signal .

Using this flag you can handle the closure, in the example of a series of threads, in a safe way by not using global variables

-1

You can use a signal handler which is a method of a class. Then that handler can access member data from that class. I'm not entirely sure what Python does under the covers here around the C signal() call, but it must be re-scoping data?

I was amazed that this works, but it does. Run this and then kill the process from another terminal.

import os, signal, time

class someclass:
    def __init__(self):
        self.myvalue = "something initialized not globally defined"
        signal.signal(signal.SIGTERM, self.myHandler)
    def myHandler(self, s, f):
        # WTF u can do this?
        print "HEY I CAUGHT IT, AND CHECK THIS OUT", self.myvalue


print "Making an object"
a = someclass()

while 1:
    print "sleeping.  Kill me now."
    time.sleep(60)
  • 5
    The question was about C ... Though Python most likely remembers the signal somewhere to process it later as soon as possible (it's dangerous to do things inside of signal handlers). Possibly using a global variable. – MauganRa Mar 02 '14 at 03:05