Linux C: upon receiving a signal, is it possible to know the PID of the sender?
Asked Answered
C

4

19

Suppose my C program handles SIGUSR1.

When it receives this signal, is it possible to know who sent it? I.e., How to get the pid of sender process?

Carmelocarmen answered 16/7, 2012 at 16:21 Comment(1)
Does this answer your question? How can I tell in Linux which process sent my process a signalJosephson
P
17

Yes, if you use the sigaction() call to set up your signal handler instead of signal. Doing so will let you set up a signal handler that takes three parameters:

  • An int, for the signal number (just like signal)
  • A siginfo_t *, which is a structure containing all sorts of information about the source of the signal, including the pid of the sender if applicable. (It also includes some information about the cause of the signal for automatic signals like SIGSEGV.)
  • A ucontext_t *, which has to do with which thread got the signal. Mostly ignorable.
Proptosis answered 16/7, 2012 at 16:47 Comment(0)
M
11

Yes. Register your signal handler using sigaction with the SA_SIGINFO flag, filling in the sa_sigaction field. Now your handler function takes a siginfo_t* parameter, which includes a field si_pid.

Note that si_pid is only set under some circumstances. In your case, you'll want to check that check that si_code is set to SI_USER or SI_QUEUE. Read man 2 sigaction for more.

Mozzetta answered 16/7, 2012 at 16:48 Comment(0)
M
4

Another option is using signalfd(). If you use signals to send information between processes, then a more structured signal handling than signal handlers is most likely what you want. struct signalfd_siginfo::ssi_pid is the sender.

Example from the man page:

#include <sys/signalfd.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[]) {
    sigset_t mask;
    int sfd;
    struct signalfd_siginfo fdsi;
    ssize_t s;

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGQUIT);

    /* Block signals so that they aren't handled
       according to their default dispositions */

    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
        handle_error("sigprocmask");

    sfd = signalfd(-1, &mask, 0);
    if (sfd == -1)
        handle_error("signalfd");

    for (;;) {
        s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
        if (s != sizeof(struct signalfd_siginfo))
            handle_error("read");

        if (fdsi.ssi_signo == SIGINT) {
            printf("Got SIGINT\n");
        } else if (fdsi.ssi_signo == SIGQUIT) {
            printf("Got SIGQUIT\n");
            exit(EXIT_SUCCESS);
        } else {
            printf("Read unexpected signal\n");
        }
    }
}

See also: sigqueue(). Like kill(), but you can pass an integer or pointer in the same call.

Mundt answered 28/3, 2017 at 12:54 Comment(0)
E
2

Here's a complete example of the POSIX-standard sigaction() API:

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void sigusr1(int signo, siginfo_t *si, void *data) {
  (void)signo;
  (void)data;
  printf("Signal %d from pid %lu\n", (int)si->si_signo,
         (unsigned long)si->si_pid);
  exit(0);
}

int main(void) {
  struct sigaction sa;
  memset(&sa, 0, sizeof(sa));
  sa.sa_flags = SA_SIGINFO;
  sa.sa_sigaction = sigusr1;
  if (sigaction(SIGUSR1, &sa, 0) == -1) {
    fprintf(stderr, "%s: %s\n", "sigaction", strerror(errno));
  }
  printf("Pid %lu waiting for SIGUSR1\n", (unsigned long)getpid());
  for (;;) {
    sleep(10);
  }
  return 0;
}

Try to run it and then send it SIGUSR1 (e.g. kill -SIGUSR1 that-pid from a shell)

sigaction function: http://pubs.opengroup.org/onlinepubs/009604499/functions/sigaction.html

siginfo structure: http://pubs.opengroup.org/onlinepubs/009604499/basedefs/signal.h.html

Espy answered 3/5, 2018 at 8:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.