41

How can I quickly get signal name from its number? There is strsignal(), but I just want the name, e.g. SIGUSR1

In other words, if we have macros like SIGUSR1 -> 12 do we have anything like 12 -> SIGUSR1 ?

Mike Roll
  • 928
  • 2
  • 7
  • 19
  • 3
    In my `/usr/include/sys/signal.h`, all of the signal names are just `#define`d constants. That means the preprocessor has already replaced them with constant integers by the time the compiler runs - the symbolic name *doesn't exist* as far as the program is concerned. What exactly are you trying to do here? – Carl Norum May 12 '13 at 16:37
  • @CarlNorum, nothing special, just fancy output. I thought the actual names could be stored somewhere else so I could get them. – Mike Roll May 12 '13 at 16:42
  • If you want "fancy output", isn't that exactly what `strsignal` is for? – Carl Norum May 12 '13 at 16:43
  • 1
    Yeah it's fancy but not the way I want it :) Just curious, not an issue at all. – Mike Roll May 12 '13 at 16:46
  • 3
    My `strsignal` man page says you can get the names directly from `sys_signame`. – Carl Norum May 12 '13 at 16:48
  • @CarlNorum That one exactly. Thanks a lot! Didn't find this in my man page though. – Mike Roll May 12 '13 at 16:51
  • OK, I'll write an answer. – Carl Norum May 12 '13 at 16:54

5 Answers5

28

My strsignal(3) man page says you can get the names directly from the sys_signame array. Here's a simple example program I wrote to test it:

#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

void upcase(char *s)
{
    while (*s)
    {
        *s = toupper(*s);
        s++;        
    }
}

int main(void)
{    
    for (int sig = 1; sig < NSIG; sig++)
    {
        char *str = strdup(sys_signame[sig]);
        if (!str)
            return -1;

        upcase(str);
        printf("%2d -> SIG%s\n", sig, str);

        free(str);
    }

    return 0;
}

I think this program produces the output you're looking for:

$ ./example 
 1 -> SIGHUP
 2 -> SIGINT
 3 -> SIGQUIT
 4 -> SIGILL
 5 -> SIGTRAP
 6 -> SIGABRT
 7 -> SIGEMT
 8 -> SIGFPE
 9 -> SIGKILL
10 -> SIGBUS
11 -> SIGSEGV
12 -> SIGSYS
13 -> SIGPIPE
14 -> SIGALRM
15 -> SIGTERM
16 -> SIGURG
17 -> SIGSTOP
18 -> SIGTSTP
19 -> SIGCONT
20 -> SIGCHLD
21 -> SIGTTIN
22 -> SIGTTOU
23 -> SIGIO
24 -> SIGXCPU
25 -> SIGXFSZ
26 -> SIGVTALRM
27 -> SIGPROF
28 -> SIGWINCH
29 -> SIGINFO
30 -> SIGUSR1
31 -> SIGUSR2
Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 8
    Only `strsignal` seems to be defined by POSIX it seems, array declarations like `sys_signame` seem to be extension. – Jens Gustedt May 12 '13 at 20:53
  • 2
    So this is perhaps an answer to the problem the OP has if he's on OS X, but not an answer to the question as it is formulated. – Jens Gustedt May 13 '13 at 06:24
  • 1
    ⁺¹, but note that array name was probably changed — it's [sys_siglist](http://man7.org/linux/man-pages/man3/strsignal.3.html). – Hi-Angel Oct 16 '18 at 14:10
6

glib 2.32 (released on 2020-08-05) introduced the function sigabbrev_np(int). Since that version you cannot use sys_siglist[] anymore too.

From man strsignal:

The sigabbrev_np() function returns the abbreviated name of the signal, sig. For example, given the value SIGINT, it returns the string "INT".
[...]
sigdescr_np() and sigdabbrev_np() first appeared in glibc 2.32. Starting with version 2.32, the sys_siglist symbol is no longer exported by glibc.

And from the release notes:

The functions sigabbrev_np and sigdescr_np have been added. The sigabbrev_np returns the abbreviated signal name (e.g. "HUP" for SIGHUP) [...] both functions return NULL for an invalid signal number.

They should be used instead of sys_siglist or sys_sigabbrev and they are both thread and async-signal safe. These functions are GNU extensions.

Socowi
  • 25,550
  • 3
  • 32
  • 54
2

As Jens Gustedt pointed out in comments years ago, sys_signame and sys_siglist are not portable.

Since this question is tagged [unix], you can most portably #ifdef your way to a mapping of names and numeric values specific to your environment. Something like:

//
//  const char * signame(int s)
//
//    return the name of the given signal number as a string,
//    or NULL if the number is unrecognized.
//
#define _POSIX_C_SOURCE 200809L
#include <signal.h>

#define SIGNAMEANDNUM(s)  { #s, s }

static struct {
  const char *name,
  int value,
} known_signals[] = {
  SIGNAMEANDNUM(SIGABRT),   // get the POSIX signals
  SIGNAMEANDNUM(SIGALRM),
  SIGNAMEANDNUM(SIGBUS),
  SIGNAMEANDNUM(SIGCHLD),
  /* ... */
  SIGNAMEANDNUM(SIGXFSZ),
#ifdef SIGUNUSUAL           // get nonstandard signals 
  SIGNAMEANDNUM(SIGUNUSUAL),
#endif
  /* ... */
};

const char *
signame(int s) {
  const char *name = NULL;

  for (int i = 0; i < sizeof(known_signals)/sizeof(*known_signals); i++) {
    if (s == known_signals[i].value) {
      name = known_signals[i].name;
      break;
    }
  }

  return name;
}

This requires some a priori knowledge of you platform, of course.

pilcrow
  • 56,591
  • 13
  • 94
  • 135
0

Tested below code on Ubuntu 16.04 and MIPS and it works fine.

#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

extern const char * const sys_siglist[];

void upcase(char *s)
{
    while (*s)
    {
        *s = toupper(*s);
        s++;
    }
}

int main(void)
{
    int sig;
    /*
       NSIG returns number of signals available in a system
       and it may vary according to platforms;Found on Ubuntu-16.04 it return 65
       where as in MIPS it is 31; Found in both the platforms it leads to core dump
       after signal 31 so limiting scanning of signal till 31 instead of using NSIG
    */
    for (sig = 1; sig < 32; sig++)
    {
        char *str = strdup(sys_siglist[sig]);
        if (!str)
            return -1;

        upcase(str);
        printf("%2d -> SIG%s\n", sig, str);

        free(str);
    }

    return 0;
}

The output of the above code on Ubuntu-16.04 (Intel x86_64 GNU/Linux):

 1 -> SIGHANGUP
 2 -> SIGINTERRUPT
 3 -> SIGQUIT
 4 -> SIGILLEGAL INSTRUCTION
 5 -> SIGTRACE/BREAKPOINT TRAP
 6 -> SIGABORTED
 7 -> SIGBUS ERROR
 8 -> SIGFLOATING POINT EXCEPTION
 9 -> SIGKILLED
10 -> SIGUSER DEFINED SIGNAL 1
11 -> SIGSEGMENTATION FAULT
12 -> SIGUSER DEFINED SIGNAL 2
13 -> SIGBROKEN PIPE
14 -> SIGALARM CLOCK
15 -> SIGTERMINATED
16 -> SIGSTACK FAULT
17 -> SIGCHILD EXITED
18 -> SIGCONTINUED
19 -> SIGSTOPPED (SIGNAL)
20 -> SIGSTOPPED
21 -> SIGSTOPPED (TTY INPUT)
22 -> SIGSTOPPED (TTY OUTPUT)
23 -> SIGURGENT I/O CONDITION
24 -> SIGCPU TIME LIMIT EXCEEDED
25 -> SIGFILE SIZE LIMIT EXCEEDED
26 -> SIGVIRTUAL TIMER EXPIRED
27 -> SIGPROFILING TIMER EXPIRED
28 -> SIGWINDOW CHANGED
29 -> SIGI/O POSSIBLE
30 -> SIGPOWER FAILURE
31 -> SIGBAD SYSTEM CALL

The output of the above code on busybox (MIPS, Cavium):

 1 -> SIGHANGUP
 2 -> SIGINTERRUPT
 3 -> SIGQUIT
 4 -> SIGILLEGAL INSTRUCTION
 5 -> SIGTRACE/BREAKPOINT TRAP
 6 -> SIGABORTED
 7 -> SIGEMT TRAP
 8 -> SIGFLOATING POINT EXCEPTION
 9 -> SIGKILLED
10 -> SIGBUS ERROR
11 -> SIGSEGMENTATION FAULT
12 -> SIGBAD SYSTEM CALL
13 -> SIGBROKEN PIPE
14 -> SIGALARM CLOCK
15 -> SIGTERMINATED
16 -> SIGUSER DEFINED SIGNAL 1
17 -> SIGUSER DEFINED SIGNAL 2
18 -> SIGCHILD EXITED
19 -> SIGPOWER FAILURE
20 -> SIGWINDOW CHANGED
21 -> SIGURGENT I/O CONDITION
22 -> SIGI/O POSSIBLE
23 -> SIGSTOPPED (SIGNAL)
24 -> SIGSTOPPED
25 -> SIGCONTINUED
26 -> SIGSTOPPED (TTY INPUT)
27 -> SIGSTOPPED (TTY OUTPUT)
28 -> SIGVIRTUAL TIMER EXPIRED
29 -> SIGPROFILING TIMER EXPIRED
30 -> SIGCPU TIME LIMIT EXCEEDED
31 -> SIGFILE SIZE LIMIT EXCEEDED
Ankit Raj
  • 917
  • 1
  • 7
  • 18
  • 1
    but there's no SIGHANGUP or SIGINTERRUPT, they're SIGHUP and SIGINT. The ones with spaces are even more obviously wrong. The only that's right here is SIGQUIT... – ilkkachu Oct 13 '21 at 17:41
-2

May be, you can declare a global array, like this

char *signame[]={"INVALID", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGPOLL", "SIGPWR", "SIGSYS", NULL};

and can use it to print signal name in signal handler, like

void sig_handler(int signum){
     printf("Received signal : %s\n", signame[signum]);
}
pbansal
  • 59
  • 1
  • 4
    This is a bad idea because most signal numbers are implementation-dependent. SIGKILL is always 9 but not all signals have fixed numbers like that. – Dietrich Epp Nov 03 '16 at 20:21
  • but it is nice to have to use in a signal handler since strsignal is not signal-safe – SRombauts Jan 04 '18 at 10:24