Search code examples
cposixmessage-queuesigaction

si_value not a member of siginfo_t inside sigaction handler using POSIX message queues


I am sure I am missing something obvious but my problem is as follows.

As a programming exercise I am trying to create an async posix message queue server to handle incoming messages asynchronously in a handler.

Now according to my documentation sigaction(2), mq_notify(3) and sigevent(7) my thought process should work. Before I post my code I'll outline my thought process on how it should be working.

  • Using sigaction and SA_SIGINFO I will set the signal handler for SIGIO.
  • Using mq_notify and struct sigevent I will tell my message queue to send SIGIO to the current process when a message arrives.
  • By placing a pointer to my mqd_t for my message queue into the struct sigevent.sigen_value.sigval_ptr I will be able to retrieve this value in my signal handler through struct siginfo.
  • Assigevent(7) tells me that when SIGEV_SIGNAL is set in sigev_notify then si_code, si_signo and si_value are set in the signal handler's siginfo. Specifically si_value is set to the value passed in for sigev_value when calling mq_nofity.
  • In my signal handler I should be able to get my message queue's mqd_t by doing something like (mqd_t *)(info->si_value.sival_ptr) where info is the siginfo_t* passed as an argument to my signal handler. I have my mqd_t in a struct mq_info_t as I will later do some more stuff with this.

Now my problem seems to be stemming from something different, but I thought I would outline my thought process in case it is somehow relevant. According to sigaction(2), siginfo_t is a large struct containing all sorts of information, including si_value.

Now in my gdb I am getting the error that my siginfo_t in my signal handler does not have a member named si_value. The same idea is mentioned in this question here.

(gdb) p *info $1 = {si_signo = 29, si_errno = 0, si_code = -3, __pad0 = 0, _sifields = {_pad = {128611, 1000, -8896, 32767, 0 }, _kill = {si_pid = 128611, si_uid = 1000}, _timer = {si_tid = 128611, si_overrun = 1000, si_sigval = { sival_int = -8896, sival_ptr = 0x7fffffffdd40}}, _rt = {si_pid = 128611, si_uid = 1000, si_sigval = {sival_int = -8896, sival_ptr = 0x7fffffffdd40}}, _sigchld = {si_pid = 128611, si_uid = 1000, si_status = -8896, si_utime = 0, si_stime = 0}, _sigfault = {si_addr = 0x3e80001f663, si_addr_lsb = -8896, _bounds = {_addr_bnd = {_lower = 0x0, _upper = 0x0}, _pkey = 0}}, _sigpoll = {si_band = 4294967424611, si_fd = -8896}, _sigsys = {_call_addr = 0x3e80001f663, _syscall = -8896, _arch = 32767}}}

Looking at where siginfo_t is defined for my build I found that the structure defined in /usr/include/bits/types/siginfo_t.h had the same structure. I am thinking this is some issue to do with the feature macros of my builds but I am do not know much in that area. My build has #define _POSIX_C_SOURCE 199309L.

Now the key components of my code are as follows

#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <mqueue.h>
#include <string.h>

typedef struct mq_info {
    mqd_t fd;
} mq_info_t;

/** Handler for receiving async messages */
void sigHandler(int signal, siginfo_t *info, void *context)
{
    char buffer[MAX_SIZE + 1];
    int must_stop = 0;
    ssize_t bytes_read;
    mq_info_t *mq_i = (mq_info_t *)(info->si_value.sival_ptr);
    mqd_t mq = mq_i->fd;

    printf("In handler\n");

    do {
        bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);

        printf("MQ received: %s\n", buffer);
    } while (bytes_read > 0);

    printf("left handler\n");
}

int openMessageQueue(char *name, long max_msg_num, long max_msg_size)
{
    mq_info_t *mq_i = calloc(1, sizeof(mq_info_t));
    struct mq_attr attr;
    struct sigaction sa;
    struct sigevent ev;
    union sigval sv = { .sival_ptr = &mq_i };

    attr.mq_flags = O_NONBLOCK; // Async
    attr.mq_maxmsg = max_msg_num;
    attr.mq_msgsize = max_msg_size;
    attr.mq_curmsgs = 0; // Num of messages currently in queue

    if (-1 == (mq_i->fd = mq_open(name, O_CREAT | O_RDONLY, 0644, &attr)))
        goto error;

    printf("queue opened\n");

    /** Setup handler for SIGIO */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = sigHandler;
    printf("Sighandler: %p\n", sigHandler);
    sigfillset(&sa.sa_mask);
    sigdelset(&sa.sa_mask, SIGIO);
    if (sigaction(SIGIO, &sa, NULL))
        goto error;

    printf("sigaction done\n");

    /** Set up process to be informed about async queue event */
    ev.sigev_notify = SIGEV_SIGNAL; // Specify a signal should be sen
    ev.sigev_signo = SIGIO; // Signal of interest
    ev.sigev_value =
        sv; // Suplementary data passed to signal handling fuction
    ev.sigev_notify_function = NULL; // Used by SIGEV_THREAD
    ev.sigev_notify_attributes = NULL; // Used by SIGEV_THREAD

    /** Register this process to receive async notifications when a new message  */
    /**     arrives on the specified message queue  */
    if (mq_notify(mq_i->fd, &ev) < 0) {
        perror("notify failed");
        goto error;
    }

    printf("notify done\n");

    return 0;

error:
    mq_unlink(name);
    return -1;
}

Thanks in advance, I hope I have provided all relevant infromation.

Arch Linux w 5.3.8 kernel


Solution

  • Look at the very bottom of bits/types/siginfo_t.h and you will see this:

    /* X/Open requires some more fields with fixed names.  */
    #define si_pid          _sifields._kill.si_pid
    #define si_uid          _sifields._kill.si_uid
    #define si_timerid      _sifields._timer.si_tid
    #define si_overrun      _sifields._timer.si_overrun
    #define si_status       _sifields._sigchld.si_status
    #define si_utime        _sifields._sigchld.si_utime
    #define si_stime        _sifields._sigchld.si_stime
    #define si_value        _sifields._rt.si_sigval
    // ...
    

    So, in your code, when you write info->si_value.sival_ptr it gets macro-expanded to info->_sifields._rt.si_sigval.sival_ptr and the compiler "proper" is happy. But GDB doesn't know about the macro definition of si_value, so you have to type that out yourself when accessing the field from the debugger.

    (Yes, this is horrible, but we can't clean it up without breaking the ABI.)

    (Why are you using #define _POSIX_C_SOURCE 199309L? It's not the immediate cause of your problem, but you will probably be happier working with a more modern conformance mode, I recommend #define _XOPEN_SOURCE 700 for new code that should be portable among current-generation Unixes.)