0

I have a possible "It Can't Be Done!" problem, which I'd like to bounce off the community.

I'm working the libIPFIX program, written in C. At the start of the code, the program sets up a Signal Handler function:

void exit_func ( int signo )
{
    if ( verbose_level && signo )
        fprintf( stderr, "\n[%s] got signo %d, bye.\n\n", progname, signo );

    ...clean up global variables...

    exit( 1 );
}

Later in main(), the signal handler is wired into the larger code:

int main (int argc, char *argv[])
{
    ...

    // signal handler
    signal( SIGKILL, exit_func );
    signal( SIGTERM, exit_func );
    signal( SIGINT,  exit_func );

    ...

    exit(1);
}

Pretty boilerplate, from what I can tell.

Here's my issue: I'm tracking a lot of extra data in my modified version of the program, using malloc() and linked lists and whatnot. When the code detects a signal, it would be great if exit_func() could call my cleanup() functions to eliminate memory leaks, blah blah blah. What I'd love is this:

void exit_func ( int signo, LLNode* myData )
{
    ...same...

    cleanUp( myData );

    exit( 1 );
}

But a reading of sigaction plus posts like these plus my own experimenting strongly suggest that there isn't a way to pass in an additional argument to exit_func(). And that the only way to do what I want is to make my data into a global variable. I'd really hate to do that for other design reasons.

So I thought I'd go for broke and just ask: Is there was way to pass an argument into exit_func()? Thanks in advance.

Pete
  • 1,511
  • 2
  • 26
  • 49
  • 2
    OT: Why care about memory leaks if the program terminates anyway? – Support Ukraine Jan 22 '19 at 20:51
  • 1
    @4386427 - Good question. I dunno, I'm trying to stick to best practices, and ALWAYS free up memory I've allocated. No other reason. – Pete Jan 22 '19 at 20:53
  • 2
    I can't answer your explicit question but to my knowledge you don't need to worry about memory leaks as any modern OS will handle the "cleanup" for you when the program terminates. – Support Ukraine Jan 22 '19 at 20:57
  • If the memory is actually leaked you can't free it anyways. Also, it's not safe to use stdio functions in signal handlers. That way lies undefined behavior. – Shawn Jan 22 '19 at 21:03
  • @Pete That's actually a bad practice. You're just slowing down your program and adding maintenance overhead. – okovko Jan 22 '19 at 21:03
  • don't think you can using `signal`. The [man page](http://man7.org/linux/man-pages/man2/signal.2.html) for `signal` indicates the second argument is a function pointer to a function that returns `void` and accepts an `int` (the signal identifier) as the only argument. You'd probably have to figure out a way to hack in a custom signal handler if you really wanted to do this, but seeing as how the OS will clean up your memory anyway, not worth it IMO unless for educational purposes. – yano Jan 22 '19 at 21:04
  • You should check that the signal was not being ignored before installing your signal handler. There's no point in installing a signal handler for `SIGKILL`; it will not be called and your process will die. You cannot catch `SIGKILL`. – Jonathan Leffler Jan 22 '19 at 21:05
  • @okovko - Huh, thanks for pointing that out. Its comments like yours which make me a better programmer – Pete Jan 22 '19 at 21:05
  • @Shawn Wow, I didn't know that... thanks! – Pete Jan 22 '19 at 21:06
  • Maybe this could be of interest: http://man7.org/linux/man-pages/man2/sigaction.2.html It's pretty complicated but a fast scan indicates that you can get more information into handler. I didn't study the details so I can't tell whether it can do what you need. – Support Ukraine Jan 22 '19 at 21:15
  • @4386427 Wow, awesome, thanks! Folks offering helpful tips like this are why I love this site. – Pete Jan 22 '19 at 21:24
  • See also [How to avoid using `printf()` in a signal handler?](https://stackoverflow.com/questions/16891019/how-to-avoid-using-printf-in-a-signal-handler/16891799#16891799) . Note that a number of the string functions (notably `strlen()`) are now signal safe according to POSIX.1:2017. Until recently (some time after the original 2008 version), they were not allowed officially. – Jonathan Leffler Jan 22 '19 at 22:40

2 Answers2

3

You cannot send arguments to a signal handler. Anything that needs to be accessed from the handler needs to be put into some kind of global struct.

Bob Shaffer
  • 635
  • 3
  • 13
  • Wow, thanks Bob. Not the answer I was hoping for, but the information I needed. Much obliged...! – Pete Jan 23 '19 at 15:53
  • 1
    Yeah. There's not much more to say. It can only get to globals and it only receives the signal number. I have found that if I am likely to need access to more than one thing there, it's usually best to just fill a (global) struct with pointers to whatever I need. – Bob Shaffer Jan 23 '19 at 16:03
2

Your original code is already wrong (even if it appears to work; it still is already undefined behavior). Read first the signal (IPC) wikipage.

The original signal handler exit_func is already wrong. Read signal(7) and signal-safety(7). Neither exit, nor fprintf, can be called, even indirectly from a signal handler, because they are not async-signal-safe (and neither is malloc).

You'll better set some volatile sig_atomic_t variable in your signal handler, and test it in your event loops (see this and POSIX documentation on signal.h). This is usual practice (and once you do that, which you have to do to be compliant with the signal(7) and signal-safety(7) rules, your question becomes irrelevant).

You might be interested by the Linux-specific signalfd(2) (it has some minor flaws. STFW for them).

The Qt documentation has a nice chapter for Calling Qt functions from Unix signal handlers. Some of the wisdom there (e.g. the pipe(7) to self trick) may apply to your program, even if it does not use Qt.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547