Search code examples
cposix

Setting signal handler for any signal in C


I'm studying C signals and I'm having trouble with an exercise that requires to write a program which sends back any signal it receives from a process back to the sender process. I thought about using the function signal() or the function sigaction(), but they ask you to specify the signum, and I would need them to handle EVERY signal my program receives. Is there any way to do so without calling the sigaction() for every signal number? Thank you in advance for your answers


Solution

  • You don't specify what OS you are using but this is simple on POSIX systems. I can't speak to Windows.

    The call you want is sigwaitinfo.

    You need to block all signals and then use sigwaitinfo to process any received signals synchronously. Sigwaitinfo's return code is the signal received and when you pass a non-null siginfo_t struct as the second parameter you get the same information you would get from a signal handler returning a siginfo_t structure, including the pid of the sending process. Take the received signal and pid and use kill to send the signal back to the originating process. Make sure you aren't sending the signal to yourself before calling kill lest you end up in a loop of sending and receiving a signal to yourself.

    #define _POSIX_C_SOURCE 200809L
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/types.h>
    
    #define errExit(msg) do {perror(msg); exit(EXIT_FAILURE);} while (0)
    
    int main(int argc, char *argv[])
    {
        siginfo_t signalInfo;
        sigset_t  allSignals;
    
        printf("echo pid = %d\n", getpid());
    
        // block every signal but SIGKILL & SIGSTOP which can't be blocked
        sigfillset(&allSignals);
    
        if (sigprocmask(SIG_SETMASK, &allSignals, NULL) == -1)
            errExit("sigprocmask");
    
        while(1)
        {
            // accept signals until SIGTERM delivered
            int sig = sigwaitinfo(&allSignals, &signalInfo);
    
            if (sig == -1)
                errExit("sigwaitinfo");
    
            if (sig == SIGTERM)
            {
                printf("buh-bye\n");
                exit(EXIT_SUCCESS);
            }
    
            printf("echo received signal %s (%d) from pid = %d\n", strsignal(sig), sig, signalInfo.si_pid);
    
            /*** NOT doing this for testing because it will kill the sending shell
    
            // echo signal back to sender but not to yourself
            // which would be stupid and lead to an endless loop
    
            if (signalInfo.si_pid != getpid())
                kill(signalInfo.si_pid, sig);
    
           ***/
        }
    }
    

    TEST

    --- terminal 1 ---

    //current shell's pid
    > echo $$
    7646
    //send signals after starting echo pgm in another terminal
    > kill -SIGUSR1 7754
    > kill -SIGUSR2 7754
    > kill -SIGTERM 7754
    

    --- terminal 2 ---

    > echosig 
    echo pid = 7754
    echo received signal User defined signal 1 (10) from pid = 7646
    echo received signal User defined signal 2 (12) from pid = 7646
    buh-bye