Search code examples
cunixsignalssetjmp

longjmp() from signal handler


I'm using the following code to try to read an input from user and timeout and exit if more than 5 seconds pass. This is accomplished through a combination of setjmp/longjmp and the SIGALRM signal.

Here's the code:

#include <stdio.h>
#include <setjmp.h>
#include <unistd.h>
#include <string.h>
#include <sys/signal.h>

jmp_buf buffer;

// this will cause t_gets() to return -2
void timeout() {
    longjmp(buffer, 1);
}

int t_gets(char* s, int t)
{
    char* ret;
    signal(SIGALRM, timeout);
    if (setjmp(buffer) != 0)
        return -2; // <--- timeout() will jump here
    alarm(t);
    // if fgets() does not return in t seconds, SIGALARM handler timeout()
    // will be called, causing t_gets() to return -2
    ret = fgets(s, 100, stdin);
    alarm(0);
    if (ret == NULL ) return -1;
    return strlen(s);
}

int main()
{
    char s[100];
    int z=t_gets(s, 5);
    printf("%d\n", z); 
}

Now, my question is if there's anything that can go wrong with this function. I've read that calling longjmp() from a signal handler can have undefined behaviour, what exactly is it refferring to?

Also, what if the alarm triggers right after fgets() returns, but before alarm(0) is called? Will it cause the function to return -2 even if the user did input something?

LATER EDIT: I'm not interested in ways to improve the code. I just want to know how it might fail.


Solution

  • From the man page for longjmp:

    POSIX does not specify whether longjmp() will restore the signal context. If you want to save and restore signal masks, use siglongjmp()

    Your second question: Yes, the function will return -2 because longjmp() will cause it to go to the setjmp(buffer) part, but the timing will have to be very precise.